fix(journeyitem): use JOIN FETCH to eliminate N+1 document queries
Add findByGeschichteIdWithDocument() to JourneyItemRepository with a LEFT JOIN FETCH on document. getItems() now uses this query so that all documents for a journey's items are loaded in a single SQL round-trip. toView() now reads item.getDocument() directly from the already-fetched association instead of issuing a separate documentService.getSummaryById() call per item. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,4 +29,13 @@ public interface JourneyItemRepository extends JpaRepository<JourneyItem, UUID>
|
|||||||
|
|
||||||
/** COUNT for the 100-item cap check — COUNT(*)-based, never MAX(position)-derived. */
|
/** COUNT for the 100-item cap check — COUNT(*)-based, never MAX(position)-derived. */
|
||||||
long countByGeschichteId(UUID geschichteId);
|
long countByGeschichteId(UUID geschichteId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads journey items with their linked Document in a single JOIN FETCH query,
|
||||||
|
* eliminating the N+1 SELECT that would occur when accessing item.getDocument()
|
||||||
|
* lazily for each item. Items without a document (note-only) are included via
|
||||||
|
* LEFT JOIN. Ordered by position ASC.
|
||||||
|
*/
|
||||||
|
@Query("SELECT ji FROM JourneyItem ji LEFT JOIN FETCH ji.document WHERE ji.geschichte.id = :geschichteId ORDER BY ji.position ASC")
|
||||||
|
List<JourneyItem> findByGeschichteIdWithDocument(@Param("geschichteId") UUID geschichteId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ public class JourneyItemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<JourneyItemView> getItems(UUID geschichteId) {
|
public List<JourneyItemView> getItems(UUID geschichteId) {
|
||||||
return journeyItemRepository.findByGeschichteIdOrderByPosition(geschichteId)
|
return journeyItemRepository.findByGeschichteIdWithDocument(geschichteId)
|
||||||
.stream().map(this::toView).toList();
|
.stream().map(this::toView).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,8 +206,8 @@ public class JourneyItemService {
|
|||||||
|
|
||||||
JourneyItemView toView(JourneyItem item) {
|
JourneyItemView toView(JourneyItem item) {
|
||||||
DocumentSummary docSummary = null;
|
DocumentSummary docSummary = null;
|
||||||
if (item.getDocumentId() != null) {
|
Document doc = item.getDocument();
|
||||||
Document doc = documentService.getSummaryById(item.getDocumentId());
|
if (doc != null) {
|
||||||
docSummary = toSummary(doc);
|
docSummary = toSummary(doc);
|
||||||
}
|
}
|
||||||
return new JourneyItemView(item.getId(), item.getPosition(), docSummary, item.getNote());
|
return new JourneyItemView(item.getId(), item.getPosition(), docSummary, item.getNote());
|
||||||
|
|||||||
@@ -322,7 +322,6 @@ class JourneyItemServiceTest {
|
|||||||
Document doc = makeDoc(docId, null, List.of(), null, null);
|
Document doc = makeDoc(docId, null, List.of(), null, null);
|
||||||
JourneyItem item = savedItemWithDoc(itemId, journey, 10, doc, "Old note");
|
JourneyItem item = savedItemWithDoc(itemId, journey, 10, doc, "Old note");
|
||||||
when(journeyItemRepository.findByIdAndGeschichteId(itemId, geschichteId)).thenReturn(Optional.of(item));
|
when(journeyItemRepository.findByIdAndGeschichteId(itemId, geschichteId)).thenReturn(Optional.of(item));
|
||||||
when(documentService.getSummaryById(docId)).thenReturn(doc);
|
|
||||||
JourneyItem saved = savedItemWithDoc(itemId, journey, 10, doc, null);
|
JourneyItem saved = savedItemWithDoc(itemId, journey, 10, doc, null);
|
||||||
when(journeyItemRepository.save(item)).thenReturn(saved);
|
when(journeyItemRepository.save(item)).thenReturn(saved);
|
||||||
|
|
||||||
@@ -372,7 +371,6 @@ class JourneyItemServiceTest {
|
|||||||
Document doc = makeDoc(docId, null, List.of(), null, null);
|
Document doc = makeDoc(docId, null, List.of(), null, null);
|
||||||
JourneyItem item = savedItemWithDoc(itemId, journey, 10, doc, "Old");
|
JourneyItem item = savedItemWithDoc(itemId, journey, 10, doc, "Old");
|
||||||
when(journeyItemRepository.findByIdAndGeschichteId(itemId, geschichteId)).thenReturn(Optional.of(item));
|
when(journeyItemRepository.findByIdAndGeschichteId(itemId, geschichteId)).thenReturn(Optional.of(item));
|
||||||
when(documentService.getSummaryById(docId)).thenReturn(doc);
|
|
||||||
JourneyItem saved = savedItemWithDoc(itemId, journey, 10, doc, null);
|
JourneyItem saved = savedItemWithDoc(itemId, journey, 10, doc, null);
|
||||||
when(journeyItemRepository.save(item)).thenReturn(saved);
|
when(journeyItemRepository.save(item)).thenReturn(saved);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user