fix(timeline): add @Transactional(readOnly=true) to TimelineService.assemble()
Without the annotation, Hibernate closes its sub-transaction after eventRepository.findAll() returns, leaving TimelineEvent entities detached. Accessing ev.getPersons() or doc.getReceivers() on those detached entities throws LazyInitializationException in production (constitution §1.6). @DataJpaTest and @Transactional test classes masked the bug by keeping an outer session alive. Fixes: @felix / @markus review blockers on PR #826 Refs #777 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import org.raddatz.familienarchiv.person.Person;
|
||||
import org.raddatz.familienarchiv.person.PersonService;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
@@ -56,7 +57,14 @@ public class TimelineService {
|
||||
* Assembles the timeline for the given filter. All filters are ANDed.
|
||||
* Throws {@link DomainException} (bad request) when fromYear > toYear.
|
||||
* Throws {@link DomainException} (not found) when personId refers to an unknown person.
|
||||
*
|
||||
* <p>{@code @Transactional(readOnly=true)} is required here — unlike simple scalar reads,
|
||||
* this method accesses lazy collections ({@link TimelineEvent#getPersons()},
|
||||
* {@link org.raddatz.familienarchiv.document.Document#getReceivers()}) after the
|
||||
* repository sub-transaction closes. Without this annotation those accesses throw
|
||||
* {@link org.hibernate.LazyInitializationException} in production (constitution §1.6).
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public TimelineDTO assemble(TimelineFilter filter) {
|
||||
if (filter.fromYear() != null && filter.toYear() != null
|
||||
&& filter.fromYear() > filter.toYear()) {
|
||||
|
||||
Reference in New Issue
Block a user