Step 04 — Service layer + transazioni¶
Obiettivo: spostare la logica fuori dal controller in un service transazionale.
Cosa costruiamo¶
ProjectService e TaskService con annotazioni transazionali e mapper dedicati.
ProjectService.java
@Service
@Transactional
public class ProjectService {
private final ProjectRepository repository;
private final ProjectMapper mapper;
public ProjectService(ProjectRepository repository, ProjectMapper mapper) {
this.repository = repository;
this.mapper = mapper;
}
@Transactional(readOnly = true)
public Page<ProjectResponse> list(ProjectStatus status, Pageable pageable) {
Page<Project> page = (status == null)
? repository.findAll(pageable)
: repository.findByStatus(status, pageable);
return page.map(mapper::toResponse);
}
public ProjectResponse archive(UUID id) {
Project project = loadOrThrow(id);
project.archive();
return mapper.toResponse(project); // (1)
}
}
- Niente
repository.save(project): dentro la transazione, Hibernate fa il dirty checking e fa flush in automatico al commit.
Concetti chiave¶
@Transactionala livello classe (write) +@Transactional(readOnly = true)sulle letture.- Il service non sa nulla di HTTP: lavora con DTO e domain entity, non con
ResponseEntity. - Mapper manuale (
ProjectMapper) invece di MapStruct — sufficiente per progetti piccoli.
Pitfall¶
@Transactional su metodi private
Non funziona: il proxy Spring intercetta solo metodi public. Errore comune.
Modificare l'entità fuori dalla transazione
Hibernate non vede modifiche. Sempre dentro un metodo @Transactional.
Esercizi¶
- Aggiungi un metodo
bulkArchive(List<UUID>)con transazione singola. - Spiega cosa cambia con
propagation = REQUIRES_NEW. - Sostituisci
ProjectMappercon un'implementazione MapStruct e confronta i tempi di build.