Skip to content

Commit d7cd8f9

Browse files
fladdimirgsmet
authored andcommitted
fix entity-manager retrieval in spring-data-repos
when using multiple persistence-units: - save a detached entity (merge) - getOne - paginated queries - deleteAll closes 38319 (cherry picked from commit e7bd343)
1 parent a4da57e commit d7cd8f9

File tree

6 files changed

+131
-21
lines changed

6 files changed

+131
-21
lines changed

extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/AdditionalJpaOperations.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class AdditionalJpaOperations {
3232
public static PanacheQuery<?> find(AbstractJpaOperations<?> jpaOperations, Class<?> entityClass, String query,
3333
String countQuery, Sort sort, Map<String, Object> params) {
3434
String findQuery = createFindQuery(entityClass, query, jpaOperations.paramCount(params));
35-
EntityManager em = jpaOperations.getEntityManager();
35+
EntityManager em = jpaOperations.getEntityManager(entityClass);
3636
Query jpaQuery = em.createQuery(sort != null ? findQuery + toOrderBy(sort) : findQuery);
3737
JpaOperations.bindParameters(jpaQuery, params);
3838
return new CustomCountPanacheQuery(em, jpaQuery, countQuery, params);
@@ -47,14 +47,14 @@ public static PanacheQuery<?> find(AbstractJpaOperations<?> jpaOperations, Class
4747
public static PanacheQuery<?> find(AbstractJpaOperations<?> jpaOperations, Class<?> entityClass, String query,
4848
String countQuery, Sort sort, Object... params) {
4949
String findQuery = createFindQuery(entityClass, query, jpaOperations.paramCount(params));
50-
EntityManager em = jpaOperations.getEntityManager();
50+
EntityManager em = jpaOperations.getEntityManager(entityClass);
5151
Query jpaQuery = em.createQuery(sort != null ? findQuery + toOrderBy(sort) : findQuery);
5252
JpaOperations.bindParameters(jpaQuery, params);
5353
return new CustomCountPanacheQuery(em, jpaQuery, countQuery, params);
5454
}
5555

5656
public static long deleteAllWithCascade(AbstractJpaOperations<?> jpaOperations, Class<?> entityClass) {
57-
EntityManager em = jpaOperations.getEntityManager();
57+
EntityManager em = jpaOperations.getEntityManager(entityClass);
5858
//detecting the case where there are cascade-delete associations, and do the bulk delete query otherwise.
5959
if (deleteOnCascadeDetected(jpaOperations, entityClass)) {
6060
int count = 0;
@@ -77,7 +77,7 @@ public static long deleteAllWithCascade(AbstractJpaOperations<?> jpaOperations,
7777
* @return true if cascading delete is needed. False otherwise
7878
*/
7979
private static boolean deleteOnCascadeDetected(AbstractJpaOperations<?> jpaOperations, Class<?> entityClass) {
80-
EntityManager em = jpaOperations.getEntityManager();
80+
EntityManager em = jpaOperations.getEntityManager(entityClass);
8181
Metamodel metamodel = em.getMetamodel();
8282
EntityType<?> entity1 = metamodel.entity(entityClass);
8383
Set<Attribute<?, ?>> declaredAttributes = ((EntityTypeImpl) entity1).getDeclaredAttributes();
@@ -96,7 +96,7 @@ private static boolean deleteOnCascadeDetected(AbstractJpaOperations<?> jpaOpera
9696

9797
public static <PanacheQueryType> long deleteWithCascade(AbstractJpaOperations<PanacheQueryType> jpaOperations,
9898
Class<?> entityClass, String query, Object... params) {
99-
EntityManager em = jpaOperations.getEntityManager();
99+
EntityManager em = jpaOperations.getEntityManager(entityClass);
100100
if (deleteOnCascadeDetected(jpaOperations, entityClass)) {
101101
int count = 0;
102102
List<?> objects = jpaOperations.list(jpaOperations.find(entityClass, query, params));
@@ -112,7 +112,7 @@ public static <PanacheQueryType> long deleteWithCascade(AbstractJpaOperations<Pa
112112
public static <PanacheQueryType> long deleteWithCascade(AbstractJpaOperations<PanacheQueryType> jpaOperations,
113113
Class<?> entityClass, String query,
114114
Map<String, Object> params) {
115-
EntityManager em = jpaOperations.getEntityManager();
115+
EntityManager em = jpaOperations.getEntityManager(entityClass);
116116
if (deleteOnCascadeDetected(jpaOperations, entityClass)) {
117117
int count = 0;
118118
List<?> objects = jpaOperations.list(jpaOperations.find(entityClass, query, params));

extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/StockMethodsAdder.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr
8787
// and if so generate the implementation while also keeping the proper records
8888

8989
generateSave(classCreator, generatedClassName, entityDotName, entityTypeStr,
90-
allMethodsToBeImplementedToResult);
90+
allMethodsToBeImplementedToResult, entityClassFieldDescriptor);
9191
generateSaveAndFlush(classCreator, generatedClassName, entityDotName, entityTypeStr,
92-
allMethodsToBeImplementedToResult);
92+
allMethodsToBeImplementedToResult, entityClassFieldDescriptor);
9393
generateSaveAll(classCreator, entityClassFieldDescriptor, generatedClassName, entityDotName, entityTypeStr,
9494
allMethodsToBeImplementedToResult);
9595
generateFlush(classCreator, generatedClassName, allMethodsToBeImplementedToResult);
@@ -121,7 +121,8 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr
121121

122122
private void generateSave(ClassCreator classCreator, String generatedClassName,
123123
DotName entityDotName, String entityTypeStr,
124-
Map<MethodDescriptor, Boolean> allMethodsToBeImplementedToResult) {
124+
Map<MethodDescriptor, Boolean> allMethodsToBeImplementedToResult,
125+
FieldDescriptor entityClassFieldDescriptor) {
125126

126127
MethodDescriptor saveDescriptor = MethodDescriptor.ofMethod(generatedClassName, "save", entityTypeStr,
127128
entityTypeStr);
@@ -144,7 +145,7 @@ private void generateSave(ClassCreator classCreator, String generatedClassName,
144145
entity);
145146
BranchResult isNewBranch = save.ifTrue(isNew);
146147
generatePersistAndReturn(entity, isNewBranch.trueBranch());
147-
generateMergeAndReturn(entity, isNewBranch.falseBranch());
148+
generateMergeAndReturn(entity, isNewBranch.falseBranch(), entityClassFieldDescriptor);
148149
} else {
149150
AnnotationTarget idAnnotationTarget = getIdAnnotationTarget(entityDotName, index);
150151
ResultHandle idValue = generateObtainValue(save, entityDotName, entity, idAnnotationTarget);
@@ -167,7 +168,7 @@ private void generateSave(ClassCreator classCreator, String generatedClassName,
167168
versionValueTarget.get());
168169
BranchResult versionValueIsNullBranch = save.ifNull(versionValue);
169170
generatePersistAndReturn(entity, versionValueIsNullBranch.trueBranch());
170-
generateMergeAndReturn(entity, versionValueIsNullBranch.falseBranch());
171+
generateMergeAndReturn(entity, versionValueIsNullBranch.falseBranch(), entityClassFieldDescriptor);
171172
}
172173

173174
BytecodeCreator idValueUnset;
@@ -192,7 +193,7 @@ private void generateSave(ClassCreator classCreator, String generatedClassName,
192193
idValueUnset = idValueNullBranch.trueBranch();
193194
}
194195
generatePersistAndReturn(entity, idValueUnset);
195-
generateMergeAndReturn(entity, idValueSet);
196+
generateMergeAndReturn(entity, idValueSet, entityClassFieldDescriptor);
196197
}
197198
}
198199
try (MethodCreator bridgeSave = classCreator.getMethodCreator(bridgeSaveDescriptor)) {
@@ -236,10 +237,13 @@ private void generatePersistAndReturn(ResultHandle entity, BytecodeCreator bytec
236237
bytecodeCreator.returnValue(entity);
237238
}
238239

239-
private void generateMergeAndReturn(ResultHandle entity, BytecodeCreator bytecodeCreator) {
240+
private void generateMergeAndReturn(ResultHandle entity, BytecodeCreator bytecodeCreator,
241+
FieldDescriptor entityClassFieldDescriptor) {
242+
ResultHandle entityClass = bytecodeCreator.readInstanceField(entityClassFieldDescriptor, bytecodeCreator.getThis());
240243
ResultHandle entityManager = bytecodeCreator.invokeVirtualMethod(
241-
ofMethod(AbstractJpaOperations.class, "getEntityManager", EntityManager.class),
242-
bytecodeCreator.readStaticField(operationsField));
244+
ofMethod(AbstractJpaOperations.class, "getEntityManager", EntityManager.class, Class.class),
245+
bytecodeCreator.readStaticField(operationsField),
246+
entityClass);
243247
entity = bytecodeCreator.invokeInterfaceMethod(
244248
MethodDescriptor.ofMethod(EntityManager.class, "merge", Object.class, Object.class),
245249
entityManager, entity);
@@ -280,7 +284,7 @@ private Type getTypeOfTarget(AnnotationTarget idAnnotationTarget) {
280284

281285
private void generateSaveAndFlush(ClassCreator classCreator,
282286
String generatedClassName, DotName entityDotName, String entityTypeStr,
283-
Map<MethodDescriptor, Boolean> allMethodsToBeImplementedToResult) {
287+
Map<MethodDescriptor, Boolean> allMethodsToBeImplementedToResult, FieldDescriptor entityClassFieldDescriptor) {
284288

285289
MethodDescriptor saveAndFlushDescriptor = MethodDescriptor.ofMethod(generatedClassName, "saveAndFlush", entityTypeStr,
286290
entityTypeStr);
@@ -298,7 +302,7 @@ private void generateSaveAndFlush(ClassCreator classCreator,
298302
// we need to force the generation of findById since this method depends on it
299303
allMethodsToBeImplementedToResult.put(save, false);
300304
generateSave(classCreator, generatedClassName, entityDotName, entityTypeStr,
301-
allMethodsToBeImplementedToResult);
305+
allMethodsToBeImplementedToResult, entityClassFieldDescriptor);
302306

303307
try (MethodCreator saveAndFlush = classCreator.getMethodCreator(saveAndFlushDescriptor)) {
304308
saveAndFlush.addAnnotation(Transactional.class);

extensions/spring-data-jpa/deployment/src/test/java/io/quarkus/spring/data/deployment/multiple_pu/MultiplePersistenceUnitConfigTest.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
package io.quarkus.spring.data.deployment.multiple_pu;
22

3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.util.function.Supplier;
6+
7+
import jakarta.inject.Inject;
8+
39
import org.hamcrest.Matchers;
10+
import org.junit.jupiter.api.BeforeEach;
411
import org.junit.jupiter.api.Test;
512
import org.junit.jupiter.api.extension.RegisterExtension;
13+
import org.springframework.data.domain.PageRequest;
14+
import org.springframework.data.domain.Pageable;
15+
import org.springframework.data.domain.Sort;
616

17+
import io.quarkus.narayana.jta.QuarkusTransaction;
718
import io.quarkus.spring.data.deployment.multiple_pu.first.FirstEntity;
819
import io.quarkus.spring.data.deployment.multiple_pu.first.FirstEntityRepository;
920
import io.quarkus.spring.data.deployment.multiple_pu.second.SecondEntity;
@@ -21,6 +32,17 @@ public class MultiplePersistenceUnitConfigTest {
2132
PanacheTestResource.class)
2233
.addAsResource("application-multiple-persistence-units.properties", "application.properties"));
2334

35+
@Inject
36+
private FirstEntityRepository repository1;
37+
@Inject
38+
private SecondEntityRepository repository2;
39+
40+
@BeforeEach
41+
void beforeEach() {
42+
repository1.deleteAll();
43+
repository2.deleteAll();
44+
}
45+
2446
@Test
2547
public void panacheOperations() {
2648
/**
@@ -35,4 +57,64 @@ public void panacheOperations() {
3557
RestAssured.when().get("/persistence-unit/second/name-1").then().body(Matchers.is("1"));
3658
RestAssured.when().get("/persistence-unit/second/name-2").then().body(Matchers.is("2"));
3759
}
60+
61+
@Test
62+
public void entityLifecycle() {
63+
var detached = repository2.save(new SecondEntity());
64+
assertThat(detached.id).isNotNull();
65+
assertThat(inTx(repository2::count)).isEqualTo(1);
66+
67+
detached.name = "name";
68+
repository2.save(detached);
69+
assertThat(inTx(repository2::count)).isEqualTo(1);
70+
71+
inTx(() -> {
72+
var lazyRef = repository2.getOne(detached.id);
73+
assertThat(lazyRef.name).isEqualTo(detached.name);
74+
return null;
75+
});
76+
77+
repository2.deleteByName("otherThan" + detached.name);
78+
assertThat(inTx(() -> repository2.findById(detached.id))).isPresent();
79+
80+
repository2.deleteByName(detached.name);
81+
assertThat(inTx(() -> repository2.findById(detached.id))).isEmpty();
82+
}
83+
84+
@Test
85+
void pagedQueries() {
86+
var newEntity = new SecondEntity();
87+
newEntity.name = "name";
88+
var detached = repository2.save(newEntity);
89+
90+
Pageable pageable = PageRequest.of(0, 10, Sort.Direction.DESC, "id");
91+
92+
var page = inTx(() -> repository2.findByName(detached.name, pageable));
93+
assertThat(page.getContent()).extracting(e -> e.id).containsExactly(detached.id);
94+
95+
var pageIndexParam = inTx(() -> repository2.findByNameQueryIndexed(detached.name, pageable));
96+
assertThat(pageIndexParam.getContent()).extracting(e -> e.id).containsExactly(detached.id);
97+
98+
var pageNamedParam = inTx(() -> repository2.findByNameQueryNamed(detached.name, pageable));
99+
assertThat(pageNamedParam.getContent()).extracting(e -> e.id).containsExactly(detached.id);
100+
}
101+
102+
@Test
103+
void cascading() {
104+
var newParent = new SecondEntity();
105+
newParent.name = "parent";
106+
var newChild = new SecondEntity();
107+
newChild.name = "child";
108+
newParent.child = newChild;
109+
var detachedParent = repository2.save(newParent);
110+
111+
assertThat(inTx(repository2::count)).isEqualTo(2);
112+
113+
repository2.deleteByName(detachedParent.name);
114+
assertThat(inTx(repository2::count)).isZero();
115+
}
116+
117+
private <T> T inTx(Supplier<T> action) {
118+
return QuarkusTransaction.requiringNew().call(action::get);
119+
}
38120
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package io.quarkus.spring.data.deployment.multiple_pu.second;
22

3-
import jakarta.persistence.Entity;
4-
import jakarta.persistence.GeneratedValue;
5-
import jakarta.persistence.Id;
3+
import jakarta.persistence.*;
64

75
@Entity
86
public class SecondEntity {
@@ -12,4 +10,7 @@ public class SecondEntity {
1210
public Long id;
1311

1412
public String name;
13+
14+
@OneToOne(cascade = CascadeType.ALL)
15+
public SecondEntity child;
1516
}

extensions/spring-data-jpa/deployment/src/test/java/io/quarkus/spring/data/deployment/multiple_pu/second/SecondEntityRepository.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package io.quarkus.spring.data.deployment.multiple_pu.second;
22

3+
import java.util.Optional;
4+
5+
import org.springframework.data.domain.Page;
6+
import org.springframework.data.domain.Pageable;
7+
import org.springframework.data.jpa.repository.Query;
8+
import org.springframework.data.repository.query.Param;
39
import org.springframework.stereotype.Repository;
410

511
@Repository
@@ -8,4 +14,21 @@ public interface SecondEntityRepository extends org.springframework.data.reposit
814
SecondEntity save(SecondEntity entity);
915

1016
long count();
17+
18+
Optional<SecondEntity> findById(Long id);
19+
20+
SecondEntity getOne(Long id);
21+
22+
void deleteAll();
23+
24+
void deleteByName(String name);
25+
26+
Page<SecondEntity> findByName(String name, Pageable pageable);
27+
28+
@Query(value = "SELECT se FROM SecondEntity se WHERE name=?1", countQuery = "SELECT COUNT(*) FROM SecondEntity se WHERE name=?1")
29+
Page<SecondEntity> findByNameQueryIndexed(String name, Pageable pageable);
30+
31+
@Query(value = "SELECT se FROM SecondEntity se WHERE name=:name", countQuery = "SELECT COUNT(*) FROM SecondEntity se WHERE name=:name")
32+
Page<SecondEntity> findByNameQueryNamed(@Param("name") String name, Pageable pageable);
33+
1134
}

extensions/spring-data-jpa/runtime/src/main/java/io/quarkus/spring/data/runtime/RepositorySupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public static void deleteAll(AbstractJpaOperations<PanacheQuery<?>> operations,
4242
}
4343

4444
public static Object getOne(AbstractJpaOperations<PanacheQuery<?>> operations, Class<?> entityClass, Object id) {
45-
return operations.getEntityManager().getReference(entityClass, id);
45+
return operations.getEntityManager(entityClass).getReference(entityClass, id);
4646
}
4747

4848
public static void clear(Class<?> clazz) {

0 commit comments

Comments
 (0)