feat(timeline): add PersonService.getPersonsByGeneration + DocumentService.getAllForTimeline
PersonRepository.findByGeneration(Integer) — boxed to match nullable entity field. DocumentRepository.findAllForTimeline() — Document.list entity-graph, single query. Both services delegate with one-liner methods. Refs #777 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,11 @@ public interface DocumentRepository extends JpaRepository<Document, UUID>, JpaSp
|
||||
// Prüft effizient, ob ein Dateiname schon existiert (gibt true/false zurück)
|
||||
boolean existsByOriginalFilename(String originalFilename);
|
||||
|
||||
// Bulk-fetch for global timeline path — single query with sender+receivers eager-loaded.
|
||||
@EntityGraph("Document.list")
|
||||
@Query("SELECT d FROM Document d")
|
||||
List<Document> findAllForTimeline();
|
||||
|
||||
// lazy – @BatchSize(50) fallback active; see ADR-022
|
||||
@EntityGraph("Document.full")
|
||||
List<Document> findBySenderId(UUID senderId);
|
||||
|
||||
@@ -1051,6 +1051,10 @@ public class DocumentService {
|
||||
return documentRepository.findDocumentsWithoutVersions();
|
||||
}
|
||||
|
||||
public List<Document> getAllForTimeline() {
|
||||
return documentRepository.findAllForTimeline();
|
||||
}
|
||||
|
||||
public List<Document> getDocumentsBySender(UUID senderId) {
|
||||
return documentRepository.findBySenderId(senderId);
|
||||
}
|
||||
|
||||
@@ -242,4 +242,7 @@ public interface PersonRepository extends JpaRepository<Person, UUID> {
|
||||
)
|
||||
""", nativeQuery = true)
|
||||
void insertMissingReceiverReference(@Param("source") UUID source, @Param("target") UUID target);
|
||||
|
||||
// Boxed Integer — matches the nullable person.generation column (primitive int would reject null rows).
|
||||
List<Person> findByGeneration(Integer generation);
|
||||
}
|
||||
|
||||
@@ -210,6 +210,10 @@ public class PersonService {
|
||||
return personRepository.findByFamilyMemberTrueOrderByLastNameAscFirstNameAsc();
|
||||
}
|
||||
|
||||
public List<Person> getPersonsByGeneration(Integer generation) {
|
||||
return personRepository.findByGeneration(generation);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Person setFamilyMember(UUID personId, boolean familyMember) {
|
||||
Person person = getById(personId);
|
||||
|
||||
@@ -2943,4 +2943,17 @@ class DocumentServiceTest {
|
||||
assertThat(result.buckets()).isEmpty();
|
||||
verify(documentRepository, org.mockito.Mockito.never()).findAll(any(Specification.class));
|
||||
}
|
||||
|
||||
// --- getAllForTimeline ---
|
||||
|
||||
@Test
|
||||
void getAllForTimeline_delegates_bulk_fetch_to_repository() {
|
||||
Document doc = Document.builder().id(UUID.randomUUID()).title("Brief").build();
|
||||
when(documentRepository.findAllForTimeline()).thenReturn(List.of(doc));
|
||||
|
||||
List<Document> result = documentService.getAllForTimeline();
|
||||
|
||||
assertThat(result).containsExactly(doc);
|
||||
verify(documentRepository).findAllForTimeline();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1105,4 +1105,25 @@ class PersonServiceTest {
|
||||
assertThat(result.direct()).hasSize(1);
|
||||
assertThat(result.partial()).isEmpty();
|
||||
}
|
||||
|
||||
// --- getPersonsByGeneration ---
|
||||
|
||||
@Test
|
||||
void getPersonsByGeneration_delegates_to_repository() {
|
||||
Person p = Person.builder().id(UUID.randomUUID()).lastName("Müller").generation(2).build();
|
||||
when(personRepository.findByGeneration(2)).thenReturn(List.of(p));
|
||||
|
||||
List<Person> result = personService.getPersonsByGeneration(2);
|
||||
|
||||
assertThat(result).containsExactly(p);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPersonsByGeneration_returns_emptyList_when_no_match() {
|
||||
when(personRepository.findByGeneration(99)).thenReturn(List.of());
|
||||
|
||||
List<Person> result = personService.getPersonsByGeneration(99);
|
||||
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user