test(transcription): JOIN FETCH query loads all block mentions for propagation
Add findByPersonIdWithMentionsFetched to TranscriptionBlockRepository: subquery finds blocks referencing the renamed person, outer JOIN FETCH loads their full mentionedPersons collection. Avoids N+1 lazy selects in the propagation listener. Filtered JOIN FETCH (WHERE m.personId=:personId) was rejected — it loads only one mention entry per block, risking data loss on saveAllAndFlush. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,15 @@ public interface TranscriptionBlockRepository extends JpaRepository<Transcriptio
|
|||||||
|
|
||||||
List<TranscriptionBlock> findByMentionedPersons_PersonId(UUID personId);
|
List<TranscriptionBlock> findByMentionedPersons_PersonId(UUID personId);
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT DISTINCT b FROM TranscriptionBlock b
|
||||||
|
JOIN FETCH b.mentionedPersons
|
||||||
|
WHERE b.id IN (
|
||||||
|
SELECT bb.id FROM TranscriptionBlock bb JOIN bb.mentionedPersons m WHERE m.personId = :personId
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
List<TranscriptionBlock> findByPersonIdWithMentionsFetched(@Param("personId") UUID personId);
|
||||||
|
|
||||||
void deleteByAnnotationId(UUID annotationId);
|
void deleteByAnnotationId(UUID annotationId);
|
||||||
|
|
||||||
int countByDocumentId(UUID documentId);
|
int countByDocumentId(UUID documentId);
|
||||||
|
|||||||
@@ -92,4 +92,36 @@ class TranscriptionBlockMentionsRepositoryTest {
|
|||||||
TranscriptionBlock reloaded = blockRepository.findById(saved.getId()).orElseThrow();
|
TranscriptionBlock reloaded = blockRepository.findById(saved.getId()).orElseThrow();
|
||||||
assertThat(reloaded.getMentionedPersons()).isEmpty();
|
assertThat(reloaded.getMentionedPersons()).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void findByPersonIdWithMentionsFetched_returnsOnlyBlocksReferencingPerson_withMentionsLoaded() {
|
||||||
|
UUID augusteId = UUID.randomUUID();
|
||||||
|
UUID hermannId = UUID.randomUUID();
|
||||||
|
|
||||||
|
blockRepository.saveAndFlush(TranscriptionBlock.builder()
|
||||||
|
.annotationId(annotationId).documentId(documentId)
|
||||||
|
.text("Brief von @Auguste Raddatz an @Hermann Müller.")
|
||||||
|
.sortOrder(0)
|
||||||
|
.mentionedPersons(List.of(
|
||||||
|
new PersonMention(augusteId, "Auguste Raddatz"),
|
||||||
|
new PersonMention(hermannId, "Hermann Müller")))
|
||||||
|
.build());
|
||||||
|
blockRepository.saveAndFlush(TranscriptionBlock.builder()
|
||||||
|
.annotationId(annotationId).documentId(documentId)
|
||||||
|
.text("Unrelated block without Auguste.")
|
||||||
|
.sortOrder(1)
|
||||||
|
.mentionedPersons(List.of(new PersonMention(hermannId, "Hermann Müller")))
|
||||||
|
.build());
|
||||||
|
em.clear();
|
||||||
|
|
||||||
|
List<TranscriptionBlock> result =
|
||||||
|
blockRepository.findByPersonIdWithMentionsFetched(augusteId);
|
||||||
|
|
||||||
|
assertThat(result).hasSize(1);
|
||||||
|
assertThat(result.get(0).getMentionedPersons())
|
||||||
|
.extracting(PersonMention::getPersonId, PersonMention::getDisplayName)
|
||||||
|
.containsExactlyInAnyOrder(
|
||||||
|
org.assertj.core.groups.Tuple.tuple(augusteId, "Auguste Raddatz"),
|
||||||
|
org.assertj.core.groups.Tuple.tuple(hermannId, "Hermann Müller"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user