Skip to content

Commit 96a6e01

Browse files
authored
Merge pull request quarkusio#49295 from yrodiere/i22183
Improve assignment of interfaces / `PanacheEntity` to persistence units
2 parents 150c39e + 629b9c3 commit 96a6e01

File tree

13 files changed

+261
-31
lines changed

13 files changed

+261
-31
lines changed

extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.ArrayList;
44
import java.util.Arrays;
55
import java.util.List;
6+
import java.util.Set;
67

78
import io.quarkus.deployment.annotations.BuildProducer;
89
import io.quarkus.deployment.annotations.BuildStep;
@@ -28,10 +29,18 @@ public final class HibernateEnversProcessor {
2829
@BuildStep
2930
List<AdditionalJpaModelBuildItem> addJpaModelClasses() {
3031
return Arrays.asList(
31-
new AdditionalJpaModelBuildItem("org.hibernate.envers.DefaultRevisionEntity"),
32-
new AdditionalJpaModelBuildItem("org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity"),
33-
new AdditionalJpaModelBuildItem("org.hibernate.envers.RevisionMapping"),
34-
new AdditionalJpaModelBuildItem("org.hibernate.envers.TrackingModifiedEntitiesRevisionMapping"));
32+
new AdditionalJpaModelBuildItem("org.hibernate.envers.DefaultRevisionEntity",
33+
// Added to specific PUs at static init using org.hibernate.boot.spi.AdditionalMappingContributor
34+
Set.of()),
35+
new AdditionalJpaModelBuildItem("org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity",
36+
// Added to specific PUs at static init using org.hibernate.boot.spi.AdditionalMappingContributor
37+
Set.of()),
38+
new AdditionalJpaModelBuildItem("org.hibernate.envers.RevisionMapping",
39+
// Added to specific PUs at static init using org.hibernate.boot.spi.AdditionalMappingContributor
40+
Set.of()),
41+
new AdditionalJpaModelBuildItem("org.hibernate.envers.TrackingModifiedEntitiesRevisionMapping",
42+
// Added to specific PUs at static init using org.hibernate.boot.spi.AdditionalMappingContributor
43+
Set.of()));
3544
}
3645

3746
@BuildStep

extensions/hibernate-orm/deployment-spi/src/main/java/io/quarkus/hibernate/orm/deployment/spi/AdditionalJpaModelBuildItem.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.quarkus.hibernate.orm.deployment.spi;
22

33
import java.util.Objects;
4+
import java.util.Set;
45

56
import io.quarkus.builder.item.MultiBuildItem;
67

@@ -12,13 +13,38 @@
1213
public final class AdditionalJpaModelBuildItem extends MultiBuildItem {
1314

1415
private final String className;
16+
private final Set<String> persistenceUnits;
1517

18+
/**
19+
* @deprecated Use {@link AdditionalJpaModelBuildItem#AdditionalJpaModelBuildItem(String, Set)} instead,
20+
* which should fit the use case of JBeret better.
21+
*/
22+
@Deprecated(since = "3.26", forRemoval = true)
1623
public AdditionalJpaModelBuildItem(String className) {
1724
Objects.requireNonNull(className);
1825
this.className = className;
26+
this.persistenceUnits = null;
27+
}
28+
29+
/**
30+
* @param className The name of the additional class.
31+
* @param persistenceUnits The name of persistence units to which this class should be added
32+
* even if the application does not request it explicitly (e.g. using `quarkus.hibernate-orm.packages`).
33+
* Note the class can still be added to a persistence unit at static init through other means --
34+
* for example Hibernate Envers and Hibernate Search use {@link org.hibernate.boot.spi.AdditionalMappingContributor}.
35+
*/
36+
public AdditionalJpaModelBuildItem(String className, Set<String> persistenceUnits) {
37+
Objects.requireNonNull(className);
38+
Objects.requireNonNull(persistenceUnits);
39+
this.className = className;
40+
this.persistenceUnits = persistenceUnits;
1941
}
2042

2143
public String getClassName() {
2244
return className;
2345
}
46+
47+
public Set<String> getPersistenceUnits() {
48+
return persistenceUnits;
49+
}
2450
}

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ public void configurationDescriptorBuilding(
327327
List<JdbcDataSourceBuildItem> jdbcDataSources,
328328
ApplicationArchivesBuildItem applicationArchivesBuildItem,
329329
LaunchModeBuildItem launchMode,
330+
List<AdditionalJpaModelBuildItem> additionalJpaModelBuildItems,
330331
JpaModelBuildItem jpaModel,
331332
Capabilities capabilities,
332333
BuildProducer<SystemPropertyBuildItem> systemProperties,
@@ -383,7 +384,8 @@ public void configurationDescriptorBuilding(
383384

384385
if (impliedPU.shouldGenerateImpliedBlockingPersistenceUnit()) {
385386
handleHibernateORMWithNoPersistenceXml(hibernateOrmConfig, index, persistenceXmlDescriptors,
386-
jdbcDataSources, applicationArchivesBuildItem, launchMode.getLaunchMode(), jpaModel, capabilities,
387+
jdbcDataSources, applicationArchivesBuildItem, launchMode.getLaunchMode(), additionalJpaModelBuildItems,
388+
jpaModel, capabilities,
387389
systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors,
388390
reflectiveMethods, unremovableBeans, dbKindMetadataBuildItems);
389391
}
@@ -843,6 +845,7 @@ private void handleHibernateORMWithNoPersistenceXml(
843845
List<JdbcDataSourceBuildItem> jdbcDataSources,
844846
ApplicationArchivesBuildItem applicationArchivesBuildItem,
845847
LaunchMode launchMode,
848+
List<AdditionalJpaModelBuildItem> additionalJpaModelBuildItems,
846849
JpaModelBuildItem jpaModel,
847850
Capabilities capabilities,
848851
BuildProducer<SystemPropertyBuildItem> systemProperties,
@@ -891,7 +894,7 @@ private void handleHibernateORMWithNoPersistenceXml(
891894
|| hibernateOrmConfig.defaultPersistenceUnit().isAnyPropertySet();
892895

893896
Map<String, Set<String>> modelClassesAndPackagesPerPersistencesUnits = getModelClassesAndPackagesPerPersistenceUnits(
894-
hibernateOrmConfig, jpaModel, index.getIndex(), enableDefaultPersistenceUnit);
897+
hibernateOrmConfig, additionalJpaModelBuildItems, jpaModel, index.getIndex(), enableDefaultPersistenceUnit);
895898
Set<String> modelClassesAndPackagesForDefaultPersistenceUnit = modelClassesAndPackagesPerPersistencesUnits
896899
.getOrDefault(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, Collections.emptySet());
897900

@@ -1135,7 +1138,8 @@ private void enhanceEntities(final JpaModelBuildItem jpaModel,
11351138
}
11361139

11371140
public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceUnits(HibernateOrmConfig hibernateOrmConfig,
1138-
JpaModelBuildItem jpaModel, IndexView index, boolean enableDefaultPersistenceUnit) {
1141+
List<AdditionalJpaModelBuildItem> additionalJpaModelBuildItems, JpaModelBuildItem jpaModel,
1142+
IndexView index, boolean enableDefaultPersistenceUnit) {
11391143
Map<String, Set<String>> modelClassesAndPackagesPerPersistenceUnits = new HashMap<>();
11401144

11411145
boolean hasPackagesInQuarkusConfig = hasPackagesInQuarkusConfig(hibernateOrmConfig);
@@ -1228,22 +1232,34 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12281232
// also add the hierarchy to the persistence unit
12291233
// we would need to add all the underlying model to it but adding the hierarchy
12301234
// is necessary for Panache as we need to add PanacheEntity to the PU
1231-
for (String relatedModelClassName : relatedModelClassNames) {
1232-
modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).add(relatedModelClassName);
1233-
}
1235+
modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).addAll(relatedModelClassNames);
12341236
}
12351237
}
12361238
}
12371239
}
12381240

1241+
Set<String> affectedModelClasses = new HashSet<>();
1242+
for (AdditionalJpaModelBuildItem additionalJpaModel : additionalJpaModelBuildItems) {
1243+
var className = additionalJpaModel.getClassName();
1244+
var persistenceUnits = additionalJpaModel.getPersistenceUnits();
1245+
if (persistenceUnits == null) {
1246+
// Legacy behavior -- remove when the deprecated one-argument constructor of AdditionalJpaModelBuildItem gets removed.
1247+
continue;
1248+
}
1249+
affectedModelClasses.add(className); // Even if persistenceUnits is empty, the class is still affected (to nothing)
1250+
for (String persistenceUnitName : persistenceUnits) {
1251+
modelClassesAndPackagesPerPersistenceUnits.putIfAbsent(persistenceUnitName, new HashSet<>());
1252+
modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).add(className);
1253+
}
1254+
}
1255+
12391256
if (!modelClassesWithPersistenceUnitAnnotations.isEmpty()) {
12401257
throw new IllegalStateException(String.format(Locale.ROOT,
12411258
"@PersistenceUnit annotations are not supported at the class level on model classes:\n\t- %s\nUse the `.packages` configuration property or package-level annotations instead.",
12421259
String.join("\n\t- ", modelClassesWithPersistenceUnitAnnotations)));
12431260
}
12441261

1245-
Set<String> affectedModelClasses = modelClassesAndPackagesPerPersistenceUnits.values().stream().flatMap(Set::stream)
1246-
.collect(Collectors.toSet());
1262+
affectedModelClasses.addAll(modelClassesAndPackagesPerPersistenceUnits.values().stream().flatMap(Set::stream).toList());
12471263
Set<String> unaffectedModelClasses = jpaModel.getAllModelClassNames().stream()
12481264
.filter(c -> !affectedModelClasses.contains(c))
12491265
.collect(Collectors.toCollection(TreeSet::new));
@@ -1281,17 +1297,29 @@ private static Set<String> getRelatedModelClassNames(IndexView index, Set<String
12811297
return Collections.emptySet();
12821298
}
12831299

1284-
modelClassInfo = index.getClassByName(modelClassInfo.superName());
1300+
addRelatedModelClassNamesRecursively(index, knownModelClassNames, relatedModelClassNames, modelClassInfo);
12851301

1286-
while (modelClassInfo != null && !modelClassInfo.name().equals(DotNames.OBJECT)) {
1287-
String modelSuperClassName = modelClassInfo.name().toString();
1288-
if (knownModelClassNames.contains(modelSuperClassName)) {
1289-
relatedModelClassNames.add(modelSuperClassName);
1290-
}
1291-
modelClassInfo = index.getClassByName(modelClassInfo.superName());
1302+
return relatedModelClassNames;
1303+
}
1304+
1305+
private static void addRelatedModelClassNamesRecursively(IndexView index, Set<String> knownModelClassNames,
1306+
Set<String> relatedModelClassNames, ClassInfo modelClassInfo) {
1307+
if (modelClassInfo == null || modelClassInfo.name().equals(DotNames.OBJECT)) {
1308+
return;
12921309
}
12931310

1294-
return relatedModelClassNames;
1311+
String modelClassName = modelClassInfo.name().toString();
1312+
if (knownModelClassNames.contains(modelClassName)) {
1313+
relatedModelClassNames.add(modelClassName);
1314+
}
1315+
1316+
addRelatedModelClassNamesRecursively(index, knownModelClassNames, relatedModelClassNames,
1317+
index.getClassByName(modelClassInfo.superName()));
1318+
1319+
for (DotName interfaceName : modelClassInfo.interfaceNames()) {
1320+
addRelatedModelClassNamesRecursively(index, knownModelClassNames, relatedModelClassNames,
1321+
index.getClassByName(interfaceName));
1322+
}
12951323
}
12961324

12971325
private static String normalizePackage(String pakkage) {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.quarkus.hibernate.orm.config.differentpackage;
2+
3+
import io.quarkus.hibernate.orm.config.packages.ConfigEntityPUAssigmentUsingInterfaceTest;
4+
5+
/**
6+
* This must be located in a different package than the entity that implements it.
7+
* In particular, it must not be located in a subpackage of that entity's package.
8+
* Otherwise {@link ConfigEntityPUAssigmentUsingInterfaceTest} would not reproduce the problem correctly.
9+
*/
10+
public interface INamedEntity {
11+
String getName();
12+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package io.quarkus.hibernate.orm.config.packages;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.assertThatCode;
5+
6+
import java.util.logging.Formatter;
7+
import java.util.logging.Level;
8+
9+
import jakarta.enterprise.context.control.ActivateRequestContext;
10+
import jakarta.inject.Inject;
11+
import jakarta.persistence.Entity;
12+
import jakarta.persistence.GeneratedValue;
13+
import jakarta.persistence.Id;
14+
15+
import org.hibernate.Session;
16+
import org.jboss.logmanager.formatters.PatternFormatter;
17+
import org.junit.jupiter.api.Test;
18+
import org.junit.jupiter.api.extension.RegisterExtension;
19+
20+
import io.quarkus.hibernate.orm.config.differentpackage.INamedEntity;
21+
import io.quarkus.test.QuarkusUnitTest;
22+
23+
/**
24+
* Reproducer for https://github.com/quarkusio/quarkus/issues/22183
25+
*/
26+
public class ConfigEntityPUAssigmentUsingInterfaceTest {
27+
28+
private static final Formatter LOG_FORMATTER = new PatternFormatter("%s");
29+
30+
@RegisterExtension
31+
static QuarkusUnitTest runner = new QuarkusUnitTest()
32+
.withApplicationRoot((jar) -> jar
33+
.addClasses(EntityImplementingInterface.class, INamedEntity.class))
34+
// In a real-world scenario, this would be used only if there are multiple PUs,
35+
// but this is simpler and enough to reproduce the issue.
36+
.overrideConfigKey("quarkus.hibernate-orm.packages", EntityImplementingInterface.class.getPackageName())
37+
.setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue())
38+
// We don't expect any warning, in particular not:
39+
// "Could not find a suitable persistence unit for model classes:"
40+
.assertLogRecords(records -> assertThat(records).extracting(LOG_FORMATTER::format).isEmpty());
41+
42+
@Inject
43+
Session session;
44+
45+
@Test
46+
@ActivateRequestContext
47+
void smoke() {
48+
// We just want to check the lack of warnings... but let's at least check the entity works correctly.
49+
assertThatCode(() -> session.createSelectionQuery("select count(*) from EntityImplementingInterface", Long.class))
50+
.doesNotThrowAnyException();
51+
}
52+
53+
@Entity(name = "EntityImplementingInterface")
54+
public static class EntityImplementingInterface implements INamedEntity {
55+
@Id
56+
@GeneratedValue
57+
private Long id;
58+
59+
private String name;
60+
61+
public Long getId() {
62+
return id;
63+
}
64+
65+
public void setId(Long id) {
66+
this.id = id;
67+
}
68+
69+
@Override
70+
public String getName() {
71+
return name;
72+
}
73+
74+
public void setName(String name) {
75+
this.name = name;
76+
}
77+
}
78+
}

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem;
5959
import io.quarkus.hibernate.orm.deployment.PersistenceXmlDescriptorBuildItem;
6060
import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem;
61+
import io.quarkus.hibernate.orm.deployment.spi.AdditionalJpaModelBuildItem;
6162
import io.quarkus.hibernate.orm.deployment.spi.DatabaseKindDialectBuildItem;
6263
import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil;
6364
import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDescriptor;
@@ -114,6 +115,7 @@ public void buildReactivePersistenceUnit(
114115
List<JdbcDataSourceBuildItem> jdbcDataSources,
115116
ApplicationArchivesBuildItem applicationArchivesBuildItem,
116117
LaunchModeBuildItem launchMode,
118+
List<AdditionalJpaModelBuildItem> additionalJpaModelBuildItems,
117119
JpaModelBuildItem jpaModel,
118120
Capabilities capabilities,
119121
BuildProducer<SystemPropertyBuildItem> systemProperties,
@@ -157,7 +159,7 @@ public void buildReactivePersistenceUnit(
157159
enableDefaultPersistenceUnit,
158160
reactiveDataSources,
159161
jdbcDataSources,
160-
applicationArchivesBuildItem, jpaModel, launchMode, capabilities,
162+
applicationArchivesBuildItem, additionalJpaModelBuildItems, jpaModel, launchMode, capabilities,
161163
systemProperties, nativeImageResources,
162164
hotDeploymentWatchedFiles, persistenceUnitDescriptors,
163165
unremovableBeans, dbKindDialectBuildItems);
@@ -173,7 +175,7 @@ public void buildReactivePersistenceUnit(
173175

174176
producePersistenceUnitFromConfig(hibernateOrmConfig, namedPersistenceUnitName, persistenceUnitConfig, index,
175177
enableDefaultPersistenceUnit, reactiveDataSources, jdbcDataSources,
176-
applicationArchivesBuildItem, jpaModel, launchMode, capabilities,
178+
applicationArchivesBuildItem, additionalJpaModelBuildItems, jpaModel, launchMode, capabilities,
177179
systemProperties, nativeImageResources,
178180
hotDeploymentWatchedFiles, persistenceUnitDescriptors,
179181
unremovableBeans, dbKindDialectBuildItems);
@@ -186,6 +188,7 @@ private static void producePersistenceUnitFromConfig(HibernateOrmConfig hibernat
186188
List<ReactiveDataSourceBuildItem> reactiveDataSources,
187189
List<JdbcDataSourceBuildItem> jdbcDataSources,
188190
ApplicationArchivesBuildItem applicationArchivesBuildItem,
191+
List<AdditionalJpaModelBuildItem> additionalJpaModelBuildItems,
189192
JpaModelBuildItem jpaModel, LaunchModeBuildItem launchMode,
190193
Capabilities capabilities,
191194
BuildProducer<SystemPropertyBuildItem> systemProperties,
@@ -234,7 +237,7 @@ private static void producePersistenceUnitFromConfig(HibernateOrmConfig hibernat
234237
}
235238

236239
QuarkusPersistenceUnitDescriptorWithSupportedDBKind reactivePUWithDBKind = generateReactivePersistenceUnit(
237-
hibernateOrmConfig, persistenceUnitName, index, persistenceUnitConfig, jpaModel,
240+
hibernateOrmConfig, persistenceUnitName, index, persistenceUnitConfig, additionalJpaModelBuildItems, jpaModel,
238241
dbKindOptional, explicitDialect, explicitDbMinVersion, applicationArchivesBuildItem,
239242
launchMode.getLaunchMode(),
240243
systemProperties, nativeImageResources, hotDeploymentWatchedFiles, dbKindDialectBuildItems,
@@ -345,6 +348,7 @@ private static QuarkusPersistenceUnitDescriptorWithSupportedDBKind generateReact
345348
String persistenceUnitName,
346349
CombinedIndexBuildItem index,
347350
HibernateOrmConfigPersistenceUnit persistenceUnitConfig,
351+
List<AdditionalJpaModelBuildItem> additionalJpaModelBuildItems,
348352
JpaModelBuildItem jpaModel,
349353
Optional<String> dbKindOptional,
350354
Optional<String> explicitDialect,
@@ -357,7 +361,8 @@ private static QuarkusPersistenceUnitDescriptorWithSupportedDBKind generateReact
357361
List<DatabaseKindDialectBuildItem> dbKindDialectBuildItems, boolean enableDefaultPersistenceUnit) {
358362

359363
Map<String, Set<String>> modelClassesAndPackagesPerPersistencesUnits = HibernateOrmProcessor
360-
.getModelClassesAndPackagesPerPersistenceUnits(hibernateOrmConfig, jpaModel, index.getIndex(),
364+
.getModelClassesAndPackagesPerPersistenceUnits(hibernateOrmConfig, additionalJpaModelBuildItems, jpaModel,
365+
index.getIndex(),
361366
enableDefaultPersistenceUnit);
362367

363368
Set<String> modelClassesAndPackages = modelClassesAndPackagesPerPersistencesUnits

extensions/hibernate-search-orm-outbox-polling/deployment/src/main/java/io/quarkus/hibernate/search/orm/outboxpolling/deployment/HibernateSearchOutboxPollingProcessor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.List;
44
import java.util.Optional;
5+
import java.util.Set;
56

67
import org.hibernate.search.mapper.orm.outboxpolling.cfg.HibernateOrmMapperOutboxPollingSettings;
78
import org.hibernate.search.mapper.orm.outboxpolling.mapping.spi.HibernateOrmMapperOutboxPollingClasses;
@@ -37,7 +38,9 @@ void registerInternalModel(BuildProducer<AdditionalIndexedClassesBuildItem> addi
3738
.reason(getClass().getName())
3839
.methods().fields().build());
3940
for (String className : hibernateOrmTypes) {
40-
additionalJpaModel.produce(new AdditionalJpaModelBuildItem(className));
41+
additionalJpaModel.produce(new AdditionalJpaModelBuildItem(className,
42+
// Added to specific PUs at static init using org.hibernate.boot.spi.AdditionalMappingContributor
43+
Set.of()));
4144
}
4245
}
4346

0 commit comments

Comments
 (0)