- Add @BatchSize(50) fallback comments on findBySenderId / findByReceiversId
- Replace silent size() discard in getRecentActivity test with assertThat isNotEmpty()
- Add ADR-022 reference comment above @JsonIgnoreProperties on Person and Tag
- Document within-open-transaction limitation in DocumentLazyLoadingTest Javadoc
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Refactor DocumentLazyLoadingTest: pull value assertions (assertThat) out
of assertThatCode lambdas so failures surface as AssertionError rather
than "unexpected exception: AssertionError" (review item 1)
- Add @EntityGraph("Document.full") to findBySenderId, findByReceiversId,
findConversation, and findSinglePersonCorrespondence — all return full
Documents to the controller for JSON serialization (review item 2)
- Add "// Callers access only ..." comments to un-graphed methods where no
lazy associations are touched: findByTags_Id, findByStatus,
findByMetadataCompleteFalse(Sort), findByMetadataCompleteFalse(Pageable)
- Remove "what" inline comments from @Transactional(readOnly=true)
on getRecentActivity and getDocumentById — the why is in ADR-022 (item 4)
- Add named-graph coupling consequence to ADR-022: Document.java and
DocumentRepository.java graph name strings must stay in sync (item 5)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The convention 'read methods are not annotated' has one exception: methods
that return lazily-initialized entities to callers require readOnly=true to
keep the session open. Documents the rule and links to ADR-022.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace repeated personRepository.save/tagRepository.save/documentRepository.save
boilerplate with savedPerson(), savedTag(), savedDocument() helpers.
Each test body is now 2-3 lines of relevant setup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
List<Document> findAll(Specification) is called in DocumentService for
receiver-sort, sender-sort, and conversation queries but had no query-count
coverage. Asserts ≤5 statements for 5 docs with @EntityGraph(Document.list).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
assertThatCode(() -> service.searchDocuments(...)) passed vacuously on an
empty page; capture the result, assert totalElements > 0, then assert
getSender().getLastName() is accessible post-return.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without setStatisticsEnabled(true) the counter stays 0 and ≤2 passes
vacuously when the test runs in isolation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hibernate throws AnnotationException at startup when @BatchSize is placed
on a @ManyToOne field. @BatchSize is only valid on collections (@OneToMany,
@ManyToMany, @ElementCollection). The N+1 for sender is already covered by
the @EntityGraph overrides on DocumentRepository.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previous version only asserted the method call didn't throw. Now the test
captures the returned list and asserts that sender.getLastName() and
tags.size() are accessible outside the transaction, which is the scenario
that would have failed with a LazyInitializationException.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
These annotations deviate from the project convention (read methods are
normally unannotated). The comment explains that the session must stay
open for callers to access lazy-loaded collections post-return, preventing
future developers from removing the annotation as a cleanup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Consistent with the @BatchSize already on receivers and tags. Any lazy
code path not covered by an entity graph will batch-load these associations
instead of issuing one query per document.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
getRecentActivity calls findAll(Pageable) — the JpaRepository overload
not covered by the existing Specification variants. Without this override,
sender is loaded N+1 per document. Now applies Document.list graph so
sender and tags are fetched eagerly for every findAll(Pageable) call.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds failing test: findAll(Pageable) must not N+1 sender for 5 docs.
Without @EntityGraph override for this overload, each document triggers
a separate SELECT for its lazy sender.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stats tracking is already enabled per-test via setStatisticsEnabled(true);
enabling it globally added unnecessary overhead to every test in the suite.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Five integration tests verify that DocumentService and DashboardService
do not throw LazyInitializationException after the EAGER→LAZY migration:
getDocumentById, getRecentActivity, searchDocuments (receiver/sender sort),
and dashboardService.getResume.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- getDocumentById: add @Transactional(readOnly=true) — calls
tagService.resolveEffectiveColors(doc.getTags()) which requires an open
session after the LAZY switch
- getRecentActivity: add @Transactional(readOnly=true) — callers may access
tags/receivers on the returned list; keeps session open for @BatchSize fetches
- updateDocumentTags: add @Transactional — write method was missing annotation
Also adds @JsonIgnoreProperties({"hibernateLazyInitializer","handler"}) to
Person and Tag to prevent Jackson serialization errors on uninitialized
lazy proxies.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- receivers, tags, trainingLabels: FetchType.EAGER → FetchType.LAZY
- sender: add explicit FetchType.LAZY (was implicitly lazy, now explicit)
- @NamedEntityGraph("Document.full"): sender + receivers + tags
- @NamedEntityGraph("Document.list"): sender + tags
- DocumentRepository.findById overridden with @EntityGraph("Document.full")
- DocumentRepository.findAll(Specification, Pageable) overridden with
@EntityGraph("Document.list")
- DocumentRepository.findAll(Specification) overridden with
@EntityGraph("Document.list") for RECEIVER/SENDER sort paths
- @BatchSize(50) on receivers and tags as fallback for any list path
that does not go through an @EntityGraph method
Fixes issue #467.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds Hibernate statistics to the test config and two new tests in
DocumentRepositoryTest:
- findAll_withSpecAndPageable asserts ≤5 statements for 10 documents
(currently RED: EAGER @ManyToMany generates 31 secondary SELECTs)
- findById regression guard verifies collections load in ≤2 statements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>