Compare commits
6 Commits
e13b37d585
...
3358f0509f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3358f0509f | ||
|
|
667b237c33 | ||
|
|
e677756139 | ||
|
|
108ab1a288 | ||
|
|
5f83fa1bc2 | ||
|
|
0dd7d8a5e7 |
@@ -136,6 +136,7 @@ public class Document {
|
|||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "sender_id")
|
@JoinColumn(name = "sender_id")
|
||||||
|
@BatchSize(size = 50)
|
||||||
private Person sender;
|
private Person sender;
|
||||||
|
|
||||||
@ManyToMany(fetch = FetchType.LAZY)
|
@ManyToMany(fetch = FetchType.LAZY)
|
||||||
@@ -148,6 +149,7 @@ public class Document {
|
|||||||
@CollectionTable(name = "document_training_labels", joinColumns = @JoinColumn(name = "document_id"))
|
@CollectionTable(name = "document_training_labels", joinColumns = @JoinColumn(name = "document_id"))
|
||||||
@Column(name = "label")
|
@Column(name = "label")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
|
@BatchSize(size = 50)
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Set<TrainingLabel> trainingLabels = new HashSet<>();
|
private Set<TrainingLabel> trainingLabels = new HashSet<>();
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ public interface DocumentRepository extends JpaRepository<Document, UUID>, JpaSp
|
|||||||
@EntityGraph("Document.list")
|
@EntityGraph("Document.list")
|
||||||
List<Document> findAll(Specification<Document> spec);
|
List<Document> findAll(Specification<Document> spec);
|
||||||
|
|
||||||
|
@EntityGraph("Document.list")
|
||||||
|
Page<Document> findAll(Pageable pageable);
|
||||||
|
|
||||||
// Findet ein Dokument anhand des ursprünglichen Dateinamens
|
// Findet ein Dokument anhand des ursprünglichen Dateinamens
|
||||||
// Wichtig für den Abgleich beim Excel-Import & Datei-Upload
|
// Wichtig für den Abgleich beim Excel-Import & Datei-Upload
|
||||||
|
|||||||
@@ -636,7 +636,8 @@ public class DocumentService {
|
|||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0. Zuletzt aktive Dokumente (sortiert nach updatedAt DESC)
|
// @Transactional(readOnly=true) keeps the Hibernate session open so the
|
||||||
|
// lazy-loaded sender and tags on returned documents remain accessible to callers.
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<Document> getRecentActivity(int size) {
|
public List<Document> getRecentActivity(int size) {
|
||||||
return documentRepository.findAll(
|
return documentRepository.findAll(
|
||||||
@@ -845,6 +846,8 @@ public class DocumentService {
|
|||||||
documentRepository.save(doc);
|
documentRepository.save(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Transactional(readOnly=true) keeps the Hibernate session open so the
|
||||||
|
// lazy-loaded tags and receivers on the returned document remain accessible to callers.
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Document getDocumentById(UUID id) {
|
public Document getDocumentById(UUID id) {
|
||||||
Document doc = documentRepository.findById(id)
|
Document doc = documentRepository.findById(id)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
|||||||
import software.amazon.awssdk.services.s3.S3Client;
|
import software.amazon.awssdk.services.s3.S3Client;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -84,7 +85,7 @@ class DocumentLazyLoadingTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getRecentActivity_doesNotThrowLazyInitializationException() {
|
void getRecentActivity_collectionsAccessibleAfterReturn() {
|
||||||
Person sender = personRepository.save(Person.builder().firstName("Hans").lastName("RaSender").build());
|
Person sender = personRepository.save(Person.builder().firstName("Hans").lastName("RaSender").build());
|
||||||
Tag tag = tagRepository.save(Tag.builder().name("RaTag").build());
|
Tag tag = tagRepository.save(Tag.builder().name("RaTag").build());
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
@@ -96,8 +97,13 @@ class DocumentLazyLoadingTest {
|
|||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThatCode(() -> documentService.getRecentActivity(3))
|
List<Document> results = documentService.getRecentActivity(3);
|
||||||
.doesNotThrowAnyException();
|
|
||||||
|
assertThatCode(() -> {
|
||||||
|
results.forEach(d -> assertThat(d.getSender()).isNotNull());
|
||||||
|
results.forEach(d -> assertThat(d.getSender().getLastName()).isNotNull());
|
||||||
|
results.forEach(d -> d.getTags().size());
|
||||||
|
}).doesNotThrowAnyException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -560,6 +560,31 @@ class DocumentRepositoryTest {
|
|||||||
.isLessThanOrEqualTo(2);
|
.isLessThanOrEqualTo(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void findAll_withPageable_loadsSenderWithoutNPlusOne() {
|
||||||
|
Person sender = personRepository.save(Person.builder().firstName("Maria").lastName("RaSender").build());
|
||||||
|
Tag tag = tagRepository.save(Tag.builder().name("RaTag2").build());
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
documentRepository.save(Document.builder()
|
||||||
|
.title("RaDoc2 " + i).originalFilename("radoc2_" + i + ".pdf")
|
||||||
|
.status(DocumentStatus.UPLOADED)
|
||||||
|
.sender(sender)
|
||||||
|
.tags(new HashSet<>(Set.of(tag)))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
entityManager.flush();
|
||||||
|
entityManager.clear();
|
||||||
|
Statistics stats = entityManagerFactory.unwrap(SessionFactory.class).getStatistics();
|
||||||
|
stats.setStatisticsEnabled(true);
|
||||||
|
stats.clear();
|
||||||
|
|
||||||
|
documentRepository.findAll(PageRequest.of(0, 5, Sort.by(Sort.Direction.DESC, "updatedAt")));
|
||||||
|
|
||||||
|
assertThat(stats.getPrepareStatementCount())
|
||||||
|
.as("@EntityGraph(Document.list) via findAll(Pageable) must not N+1 sender for 5 docs")
|
||||||
|
.isLessThanOrEqualTo(5);
|
||||||
|
}
|
||||||
|
|
||||||
// ─── seeding helpers ─────────────────────────────────────────────────────
|
// ─── seeding helpers ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
private Document uploaded(String title) {
|
private Document uploaded(String title) {
|
||||||
|
|||||||
@@ -13,10 +13,6 @@ spring:
|
|||||||
password: test
|
password: test
|
||||||
mail:
|
mail:
|
||||||
host: localhost
|
host: localhost
|
||||||
jpa:
|
|
||||||
properties:
|
|
||||||
hibernate:
|
|
||||||
generate_statistics: true
|
|
||||||
|
|
||||||
# Disable OTel SDK entirely in tests — prevents auto-configuration from loading resource providers
|
# Disable OTel SDK entirely in tests — prevents auto-configuration from loading resource providers
|
||||||
# (e.g. AzureAppServiceResourceProvider) that fail against the semconv version used here.
|
# (e.g. AzureAppServiceResourceProvider) that fail against the semconv version used here.
|
||||||
|
|||||||
Reference in New Issue
Block a user