feat(lesereisen): data model + Flyway migration — GeschichteType, JourneyItem, migrate geschichten_documents #787

Merged
marcel merged 213 commits from feat/issue-750-lesereisen-data-model into main 2026-06-12 14:04:04 +02:00
3 changed files with 12 additions and 5 deletions
Showing only changes of commit ad90ae75bf - Show all commits

View File

@@ -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);
} }

View File

@@ -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());

View File

@@ -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);