Skip to content

Commit 51f0cf6

Browse files
committed
Integration test for multiple reactive persistence units and Panache
Backported from ORM the handling of different persistence units in entity in Panache
1 parent 8cffc19 commit 51f0cf6

File tree

18 files changed

+415
-1
lines changed

18 files changed

+415
-1
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.quarkus.hibernate.reactive.panache.deployment;
2+
3+
import io.quarkus.builder.item.MultiBuildItem;
4+
5+
/**
6+
* Used to record that a specific JPA entity is associated with a specific persistence unit
7+
*/
8+
public final class EntityToPersistenceUnitBuildItem extends MultiBuildItem {
9+
10+
private final String entityClass;
11+
private final String persistenceUnitName;
12+
13+
public EntityToPersistenceUnitBuildItem(String entityClass, String persistenceUnitName) {
14+
this.entityClass = entityClass;
15+
this.persistenceUnitName = persistenceUnitName;
16+
}
17+
18+
public String getEntityClass() {
19+
return entityClass;
20+
}
21+
22+
public String getPersistenceUnitName() {
23+
return persistenceUnitName;
24+
}
25+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package io.quarkus.hibernate.reactive.panache.deployment;
2+
3+
import java.util.Collections;
4+
import java.util.HashMap;
5+
import java.util.Map;
6+
import java.util.Optional;
7+
import java.util.Set;
8+
import java.util.TreeMap;
9+
10+
import io.quarkus.hibernate.orm.deployment.JpaModelPersistenceUnitMappingBuildItem;
11+
12+
public final class EntityToPersistenceUnitUtil {
13+
14+
private EntityToPersistenceUnitUtil() {
15+
}
16+
17+
/**
18+
* Given the candidate entities, return a map with the persistence unit that single contains them
19+
* or throw an exception if any of the candidates is part of more than one persistence unit
20+
*/
21+
public static Map<String, String> determineEntityPersistenceUnits(
22+
Optional<JpaModelPersistenceUnitMappingBuildItem> jpaModelPersistenceUnitMapping,
23+
Set<String> candidates, String source) {
24+
if (jpaModelPersistenceUnitMapping.isEmpty()) {
25+
return Collections.emptyMap();
26+
}
27+
Map<String, String> result = new HashMap<>();
28+
Map<String, Set<String>> collectedEntityToPersistenceUnits = jpaModelPersistenceUnitMapping.get()
29+
.getEntityToPersistenceUnits();
30+
31+
Map<String, Set<String>> violatingEntities = new TreeMap<>();
32+
33+
for (Map.Entry<String, Set<String>> entry : collectedEntityToPersistenceUnits.entrySet()) {
34+
String entityName = entry.getKey();
35+
Set<String> selectedPersistenceUnits = entry.getValue();
36+
boolean isCandidate = candidates.contains(entityName);
37+
38+
if (!isCandidate) {
39+
continue;
40+
}
41+
42+
if (selectedPersistenceUnits.size() == 1) {
43+
result.put(entityName, selectedPersistenceUnits.iterator().next());
44+
} else {
45+
violatingEntities.put(entityName, selectedPersistenceUnits);
46+
}
47+
}
48+
49+
if (violatingEntities.size() > 0) {
50+
StringBuilder message = new StringBuilder(
51+
String.format("%s entities do not support being attached to several persistence units:\n", source));
52+
for (Map.Entry<String, Set<String>> violatingEntityEntry : violatingEntities
53+
.entrySet()) {
54+
message.append("\t- ").append(violatingEntityEntry.getKey()).append(" is attached to: ")
55+
.append(String.join(",", violatingEntityEntry.getValue()));
56+
throw new IllegalStateException(message.toString());
57+
}
58+
}
59+
60+
return result;
61+
}
62+
}

extensions/panache/hibernate-reactive-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/deployment/PanacheHibernateResourceProcessor.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package io.quarkus.hibernate.reactive.panache.deployment;
22

3+
import static io.quarkus.hibernate.reactive.panache.deployment.EntityToPersistenceUnitUtil.determineEntityPersistenceUnits;
4+
35
import java.util.Collections;
46
import java.util.HashSet;
57
import java.util.List;
8+
import java.util.Optional;
69
import java.util.Set;
710
import java.util.stream.Collectors;
811

@@ -28,6 +31,7 @@
2831
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
2932
import io.quarkus.deployment.builditem.FeatureBuildItem;
3033
import io.quarkus.deployment.util.JandexUtil;
34+
import io.quarkus.hibernate.orm.deployment.JpaModelPersistenceUnitMappingBuildItem;
3135
import io.quarkus.hibernate.orm.deployment.spi.AdditionalJpaModelBuildItem;
3236
import io.quarkus.hibernate.reactive.panache.PanacheEntity;
3337
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase;
@@ -94,19 +98,25 @@ void collectEntityClasses(CombinedIndexBuildItem index, BuildProducer<PanacheEnt
9498
void build(CombinedIndexBuildItem index,
9599
BuildProducer<BytecodeTransformerBuildItem> transformers,
96100
List<PanacheEntityClassBuildItem> entityClasses,
97-
List<PanacheMethodCustomizerBuildItem> methodCustomizersBuildItems) throws Exception {
101+
Optional<JpaModelPersistenceUnitMappingBuildItem> jpaModelPersistenceUnitMapping,
102+
List<PanacheMethodCustomizerBuildItem> methodCustomizersBuildItems,
103+
BuildProducer<EntityToPersistenceUnitBuildItem> entityToPersistenceUnit) throws Exception {
98104

99105
List<PanacheMethodCustomizer> methodCustomizers = methodCustomizersBuildItems.stream()
100106
.map(bi -> bi.getMethodCustomizer()).collect(Collectors.toList());
101107

102108
PanacheJpaRepositoryEnhancer daoEnhancer = new PanacheJpaRepositoryEnhancer(index.getIndex());
103109
Set<String> daoClasses = new HashSet<>();
110+
Set<String> panacheEntities = new HashSet<>();
104111
for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(DOTNAME_PANACHE_REPOSITORY_BASE)) {
105112
// Skip PanacheRepository
106113
if (classInfo.name().equals(DOTNAME_PANACHE_REPOSITORY))
107114
continue;
108115
if (daoEnhancer.skipRepository(classInfo))
109116
continue;
117+
List<org.jboss.jandex.Type> typeParameters = JandexUtil
118+
.resolveTypeParameters(classInfo.name(), DOTNAME_PANACHE_REPOSITORY_BASE, index.getIndex());
119+
panacheEntities.add(typeParameters.get(0).name().toString());
110120
daoClasses.add(classInfo.name().toString());
111121
}
112122
for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(DOTNAME_PANACHE_REPOSITORY)) {
@@ -121,10 +131,20 @@ void build(CombinedIndexBuildItem index,
121131
PanacheJpaEntityOperationsEnhancer entityOperationsEnhancer = new PanacheJpaEntityOperationsEnhancer(index.getIndex(),
122132
methodCustomizers,
123133
ReactiveJavaJpaTypeBundle.BUNDLE);
134+
135+
Set<String> modelClasses = new HashSet<>();
124136
for (PanacheEntityClassBuildItem entityClass : entityClasses) {
125137
String entityClassName = entityClass.get().name().toString();
138+
modelClasses.add(entityClassName);
126139
transformers.produce(new BytecodeTransformerBuildItem(entityClassName, entityOperationsEnhancer));
127140
}
141+
142+
panacheEntities.addAll(modelClasses);
143+
144+
determineEntityPersistenceUnits(jpaModelPersistenceUnitMapping, panacheEntities, "Panache")
145+
.forEach((e, pu) -> {
146+
entityToPersistenceUnit.produce(new EntityToPersistenceUnitBuildItem(e, pu));
147+
});
128148
}
129149

130150
@BuildStep
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.quarkus.hibernate.reactive.panache.test.multiple_pu;
2+
3+
import org.hamcrest.Matchers;
4+
import org.junit.jupiter.api.Test;
5+
import org.junit.jupiter.api.extension.RegisterExtension;
6+
7+
import io.quarkus.hibernate.reactive.panache.test.multiple_pu.first.FirstEntity;
8+
import io.quarkus.hibernate.reactive.panache.test.multiple_pu.second.SecondEntity;
9+
import io.quarkus.test.QuarkusUnitTest;
10+
import io.restassured.RestAssured;
11+
12+
public class DefaultPersistenceUnitConfigTest {
13+
14+
@RegisterExtension
15+
static QuarkusUnitTest runner = new QuarkusUnitTest()
16+
.withApplicationRoot((jar) -> jar
17+
.addClasses(FirstEntity.class, SecondEntity.class, PanacheTestResource.class)
18+
.addAsResource("application.properties", "application.properties"));
19+
20+
@Test
21+
public void panacheOperations() {
22+
/**
23+
* First entity operations
24+
*/
25+
RestAssured.when().get("/persistence-unit/first/name-1").then().body(Matchers.is("1"));
26+
RestAssured.when().get("/persistence-unit/first/name-2").then().body(Matchers.is("2"));
27+
28+
/**
29+
* second entity operations
30+
*/
31+
RestAssured.when().get("/persistence-unit/second/name-1").then().body(Matchers.is("1"));
32+
RestAssured.when().get("/persistence-unit/second/name-2").then().body(Matchers.is("2"));
33+
}
34+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.quarkus.hibernate.reactive.panache.test.multiple_pu;
2+
3+
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.Test;
5+
import org.junit.jupiter.api.extension.RegisterExtension;
6+
7+
import io.quarkus.hibernate.reactive.panache.test.multiple_pu.first.FirstEntity;
8+
import io.quarkus.hibernate.reactive.panache.test.multiple_pu.second.SecondEntity;
9+
import io.quarkus.test.QuarkusUnitTest;
10+
11+
public class ErroneousPersistenceUnitConfigTest {
12+
13+
@RegisterExtension
14+
static QuarkusUnitTest runner = new QuarkusUnitTest()
15+
.setExpectedException(IllegalStateException.class)
16+
.withApplicationRoot((jar) -> jar
17+
.addClasses(FirstEntity.class, SecondEntity.class, PanacheTestResource.class)
18+
.addAsResource("application-erroneous-multiple-persistence-units.properties", "application.properties"));
19+
20+
@Test
21+
public void shouldNotReachHere() {
22+
Assertions.fail();
23+
}
24+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.quarkus.hibernate.reactive.panache.test.multiple_pu;
2+
3+
import org.hamcrest.Matchers;
4+
import org.junit.jupiter.api.Test;
5+
import org.junit.jupiter.api.extension.RegisterExtension;
6+
7+
import io.quarkus.hibernate.reactive.panache.test.multiple_pu.first.FirstEntity;
8+
import io.quarkus.hibernate.reactive.panache.test.multiple_pu.second.SecondEntity;
9+
import io.quarkus.test.QuarkusUnitTest;
10+
import io.restassured.RestAssured;
11+
12+
public class MultiplePersistenceUnitConfigTest {
13+
14+
@RegisterExtension
15+
static QuarkusUnitTest runner = new QuarkusUnitTest()
16+
.withApplicationRoot((jar) -> jar
17+
.addClasses(FirstEntity.class, SecondEntity.class, PanacheTestResource.class)
18+
.addAsResource("application-multiple-persistence-units.properties", "application.properties"));
19+
20+
@Test
21+
public void panacheOperations() {
22+
/**
23+
* First entity operations
24+
*/
25+
RestAssured.when().get("/persistence-unit/first/name-1").then().body(Matchers.is("1"));
26+
RestAssured.when().get("/persistence-unit/first/name-2").then().body(Matchers.is("2"));
27+
28+
/**
29+
* second entity operations
30+
*/
31+
RestAssured.when().get("/persistence-unit/second/name-1").then().body(Matchers.is("1"));
32+
RestAssured.when().get("/persistence-unit/second/name-2").then().body(Matchers.is("2"));
33+
}
34+
35+
// Do a WithSessionOnDemandTest with multiple persistence
36+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.quarkus.hibernate.reactive.panache.test.multiple_pu;
2+
3+
import jakarta.ws.rs.GET;
4+
import jakarta.ws.rs.Path;
5+
import jakarta.ws.rs.PathParam;
6+
import jakarta.ws.rs.Produces;
7+
import jakarta.ws.rs.core.MediaType;
8+
9+
import io.quarkus.hibernate.reactive.panache.Panache;
10+
import io.quarkus.hibernate.reactive.panache.test.multiple_pu.first.FirstEntity;
11+
import io.quarkus.hibernate.reactive.panache.test.multiple_pu.second.SecondEntity;
12+
import io.smallrye.mutiny.Uni;
13+
14+
@Path("/persistence-unit")
15+
public class PanacheTestResource {
16+
17+
@GET
18+
@Path("/first/{name}")
19+
@Produces(MediaType.TEXT_PLAIN)
20+
public Uni<Long> createWithFirstPuAndReturnCount(@PathParam("name") String name) {
21+
FirstEntity entity = new FirstEntity();
22+
entity.name = name;
23+
24+
return Panache.withTransaction(() -> entity.persistAndFlush())
25+
.flatMap(e -> FirstEntity.count());
26+
27+
}
28+
29+
@GET
30+
@Path("/second/{name}")
31+
@Produces(MediaType.TEXT_PLAIN)
32+
public Uni<Long> createWithSecondPUAndReturnCount(@PathParam("name") String name) {
33+
SecondEntity entity = new SecondEntity();
34+
entity.name = name;
35+
36+
return Panache.withTransaction(() -> entity.persistAndFlush())
37+
.flatMap(e -> SecondEntity.count());
38+
39+
}
40+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.quarkus.hibernate.reactive.panache.test.multiple_pu.first;
2+
3+
import jakarta.persistence.Entity;
4+
5+
import io.quarkus.hibernate.reactive.panache.PanacheEntity;
6+
7+
@Entity
8+
public class FirstEntity extends PanacheEntity {
9+
10+
public String name;
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.quarkus.hibernate.reactive.panache.test.multiple_pu.second;
2+
3+
import jakarta.persistence.Entity;
4+
5+
import io.quarkus.hibernate.reactive.panache.PanacheEntity;
6+
7+
@Entity
8+
public class SecondEntity extends PanacheEntity {
9+
10+
public String name;
11+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
quarkus.datasource.db-kind=postgresql
2+
quarkus.datasource.username=hibernate_orm_test
3+
quarkus.datasource.password=hibernate_orm_test
4+
quarkus.datasource.reactive.url=${postgres.reactive.url}
5+
quarkus.datasource.devservices.enabled=false
6+
7+
quarkus.hibernate-orm.schema-management.strategy=drop-and-create
8+
quarkus.hibernate-orm.packages=io.quarkus.hibernate.reactive.panache.test.multiple_pu.first
9+
10+
quarkus.datasource."second".db-kind=postgresql
11+
quarkus.datasource."second".username=hibernate_orm_test
12+
quarkus.datasource."second".password=hibernate_orm_test
13+
quarkus.datasource."second".reactive.url=${postgres.reactive.url}
14+
quarkus.datasource."second".devservices.enabled=false
15+
16+
quarkus.hibernate-orm."second".datasource=second
17+
quarkus.hibernate-orm."second".schema-management.strategy=drop-and-create
18+
quarkus.hibernate-orm."second".packages=io.quarkus.hibernate.reactive.panache.test.multiple_pu.first

0 commit comments

Comments
 (0)