Skip to content

Commit 4811438

Browse files
committed
Ignore implicit default persistence units if there is no datasource and no entity
... instead of failing as soon as there is a managed class, even if that class is not an entity. The previous behavior was particularly problematic when Panache was in the classpath, because then there will always be a managed class (Panache's mappedsuperclasses) in the default persistence unit, so we would end up failing with default configuration. Now we will just ignore such a persistence unit.
1 parent 6f0aa97 commit 4811438

File tree

4 files changed

+75
-39
lines changed

4 files changed

+75
-39
lines changed

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

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -892,33 +892,39 @@ private void handleHibernateORMWithNoPersistenceXml(
892892
&& hibernateOrmConfig.namedPersistenceUnits().isEmpty())
893893
|| hibernateOrmConfig.defaultPersistenceUnit().isAnyPropertySet();
894894

895-
Map<String, Set<String>> modelClassesAndPackagesPerPersistencesUnits = getModelClassesAndPackagesPerPersistenceUnits(
895+
var modelPerPersistencesUnit = getModelPerPersistenceUnit(
896896
hibernateOrmConfig, additionalJpaModelBuildItems, jpaModel, index.getIndex(), enableDefaultPersistenceUnit);
897-
Set<String> modelClassesAndPackagesForDefaultPersistenceUnit = modelClassesAndPackagesPerPersistencesUnits
898-
.getOrDefault(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, Collections.emptySet());
897+
var modelForDefaultPersistenceUnit = modelPerPersistencesUnit.get(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME);
898+
if (modelForDefaultPersistenceUnit == null) {
899+
modelForDefaultPersistenceUnit = new JpaPersistenceUnitModel();
900+
}
899901

900902
Set<String> storageEngineCollector = new HashSet<>();
901903

902904
if (enableDefaultPersistenceUnit) {
903905
producePersistenceUnitDescriptorFromConfig(
904906
hibernateOrmConfig, jpaModel, PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME,
905907
hibernateOrmConfig.defaultPersistenceUnit(),
906-
modelClassesAndPackagesForDefaultPersistenceUnit,
908+
modelForDefaultPersistenceUnit.allModelClassAndPackageNames(),
907909
jpaModel.getXmlMappings(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME),
908910
jdbcDataSources, applicationArchivesBuildItem, launchMode, capabilities,
909911
systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors,
910912
reflectiveMethods, unremovableBeans, storageEngineCollector, dbKindMetadataBuildItems);
911-
} else if (!modelClassesAndPackagesForDefaultPersistenceUnit.isEmpty()
913+
} else if (!modelForDefaultPersistenceUnit.entityClassNames().isEmpty()
912914
&& (!hibernateOrmConfig.defaultPersistenceUnit().datasource().isPresent()
913915
|| DataSourceUtil.isDefault(hibernateOrmConfig.defaultPersistenceUnit().datasource().get()))
914916
&& !defaultJdbcDataSource.isPresent()) {
917+
// We're not enable the default PU, meaning there is no explicit configuration for it,
918+
// and we couldn't find a default datasource.
919+
// But there are entities assigned to it, meaning these entities can never work properly.
920+
// This looks like a mistake, so we'll error out.
915921
String persistenceUnitName = PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME;
916922
String dataSourceName = DataSourceUtil.DEFAULT_DATASOURCE_NAME;
917923
var cause = DataSourceUtil.dataSourceNotConfigured(dataSourceName);
918924
throw new ConfigurationException(String.format(Locale.ROOT,
919925
"Persistence unit '%s' defines entities %s, but its datasource '%s' cannot be found: %s"
920926
+ " Alternatively, disable Hibernate ORM by setting '%s=false', and the entities will be ignored.",
921-
persistenceUnitName, modelClassesAndPackagesForDefaultPersistenceUnit,
927+
persistenceUnitName, modelForDefaultPersistenceUnit.entityClassNames(),
922928
dataSourceName,
923929
cause.getMessage(),
924930
HibernateOrmRuntimeConfig.extensionPropertyKey("enabled")),
@@ -927,11 +933,12 @@ private void handleHibernateORMWithNoPersistenceXml(
927933

928934
for (Entry<String, HibernateOrmConfigPersistenceUnit> persistenceUnitEntry : hibernateOrmConfig.namedPersistenceUnits()
929935
.entrySet()) {
936+
var persistenceUnitName = persistenceUnitEntry.getKey();
937+
var model = modelPerPersistencesUnit.get(persistenceUnitEntry.getKey());
930938
producePersistenceUnitDescriptorFromConfig(
931-
hibernateOrmConfig, jpaModel, persistenceUnitEntry.getKey(), persistenceUnitEntry.getValue(),
932-
modelClassesAndPackagesPerPersistencesUnits.getOrDefault(persistenceUnitEntry.getKey(),
933-
Collections.emptySet()),
934-
jpaModel.getXmlMappings(persistenceUnitEntry.getKey()),
939+
hibernateOrmConfig, jpaModel, persistenceUnitName, persistenceUnitEntry.getValue(),
940+
model == null ? Collections.emptySet() : model.allModelClassAndPackageNames(),
941+
jpaModel.getXmlMappings(persistenceUnitName),
935942
jdbcDataSources, applicationArchivesBuildItem, launchMode, capabilities,
936943
systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors,
937944
reflectiveMethods, unremovableBeans, storageEngineCollector, dbKindMetadataBuildItems);
@@ -1143,10 +1150,10 @@ private void enhanceEntities(final JpaModelBuildItem jpaModel,
11431150
}
11441151
}
11451152

1146-
public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceUnits(HibernateOrmConfig hibernateOrmConfig,
1153+
public static Map<String, JpaPersistenceUnitModel> getModelPerPersistenceUnit(HibernateOrmConfig hibernateOrmConfig,
11471154
List<AdditionalJpaModelBuildItem> additionalJpaModelBuildItems, JpaModelBuildItem jpaModel,
11481155
IndexView index, boolean enableDefaultPersistenceUnit) {
1149-
Map<String, Set<String>> modelClassesAndPackagesPerPersistenceUnits = new HashMap<>();
1156+
Map<String, JpaPersistenceUnitModel> modelPerPersistenceUnit = new HashMap<>();
11501157

11511158
boolean hasPackagesInQuarkusConfig = hasPackagesInQuarkusConfig(hibernateOrmConfig);
11521159
Collection<AnnotationInstance> packageLevelPersistenceUnitAnnotations = getPackageLevelPersistenceUnitAnnotations(
@@ -1212,9 +1219,11 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12121219
// No .packages configuration, no package-level persistence unit annotations,
12131220
// and no named persistence units: all the entities will be associated with the default one
12141221
// so we don't need to split them
1215-
Set<String> allModelClassesAndPackages = new HashSet<>(jpaModel.getAllModelClassNames());
1216-
allModelClassesAndPackages.addAll(jpaModel.getAllModelPackageNames());
1217-
return Collections.singletonMap(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, allModelClassesAndPackages);
1222+
var model = new JpaPersistenceUnitModel();
1223+
model.entityClassNames().addAll(jpaModel.getEntityClassNames());
1224+
model.allModelClassAndPackageNames().addAll(jpaModel.getAllModelClassNames());
1225+
model.allModelClassAndPackageNames().addAll(jpaModel.getAllModelPackageNames());
1226+
return Map.of(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, model);
12181227
}
12191228

12201229
Set<String> modelClassesWithPersistenceUnitAnnotations = new TreeSet<>();
@@ -1232,13 +1241,18 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12321241
for (Entry<String, Set<String>> packageRuleEntry : packageRules.entrySet()) {
12331242
if (modelClassName.startsWith(packageRuleEntry.getKey())) {
12341243
for (String persistenceUnitName : packageRuleEntry.getValue()) {
1235-
modelClassesAndPackagesPerPersistenceUnits.putIfAbsent(persistenceUnitName, new HashSet<>());
1236-
modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).add(modelClassName);
1244+
var model = modelPerPersistenceUnit.computeIfAbsent(persistenceUnitName,
1245+
ignored -> new JpaPersistenceUnitModel());
1246+
1247+
if (jpaModel.getEntityClassNames().contains(modelClassName)) {
1248+
model.entityClassNames().add(modelClassName);
1249+
}
1250+
model.allModelClassAndPackageNames().add(modelClassName);
12371251

12381252
// also add the hierarchy to the persistence unit
12391253
// we would need to add all the underlying model to it but adding the hierarchy
12401254
// is necessary for Panache as we need to add PanacheEntity to the PU
1241-
modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).addAll(relatedModelClassNames);
1255+
model.allModelClassAndPackageNames().addAll(relatedModelClassNames);
12421256
}
12431257
}
12441258
}
@@ -1254,8 +1268,13 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12541268
}
12551269
assignedModelClasses.add(className); // Even if persistenceUnits is empty, the class is still assigned (to nothing)
12561270
for (String persistenceUnitName : persistenceUnits) {
1257-
modelClassesAndPackagesPerPersistenceUnits.putIfAbsent(persistenceUnitName, new HashSet<>());
1258-
modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).add(className);
1271+
var model = modelPerPersistenceUnit.computeIfAbsent(persistenceUnitName,
1272+
ignored -> new JpaPersistenceUnitModel());
1273+
1274+
if (jpaModel.getEntityClassNames().contains(className)) {
1275+
model.entityClassNames().add(className);
1276+
}
1277+
model.allModelClassAndPackageNames().add(className);
12591278
}
12601279
}
12611280

@@ -1265,7 +1284,8 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12651284
String.join("\n\t- ", modelClassesWithPersistenceUnitAnnotations)));
12661285
}
12671286

1268-
assignedModelClasses.addAll(modelClassesAndPackagesPerPersistenceUnits.values().stream().flatMap(Set::stream).toList());
1287+
assignedModelClasses.addAll(modelPerPersistenceUnit.values().stream()
1288+
.map(JpaPersistenceUnitModel::allModelClassAndPackageNames).flatMap(Set::stream).toList());
12691289
Set<String> unaffectedModelClasses = jpaModel.getAllModelClassNames().stream()
12701290
.filter(c -> !assignedModelClasses.contains(c))
12711291
.collect(Collectors.toCollection(TreeSet::new));
@@ -1281,12 +1301,13 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12811301
continue;
12821302
}
12831303
for (String persistenceUnitName : persistenceUnitNames) {
1284-
modelClassesAndPackagesPerPersistenceUnits.putIfAbsent(persistenceUnitName, new HashSet<>());
1285-
modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).add(modelPackageName);
1304+
var model = modelPerPersistenceUnit.computeIfAbsent(persistenceUnitName,
1305+
ignored -> new JpaPersistenceUnitModel());
1306+
model.allModelClassAndPackageNames().add(modelPackageName);
12861307
}
12871308
}
12881309

1289-
return modelClassesAndPackagesPerPersistenceUnits;
1310+
return modelPerPersistenceUnit;
12901311
}
12911312

12921313
private static Set<String> getRelatedModelClassNames(IndexView index, Set<String> knownModelClassNames,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.quarkus.hibernate.orm.deployment;
2+
3+
import java.util.Set;
4+
import java.util.TreeSet;
5+
6+
public record JpaPersistenceUnitModel(Set<String> entityClassNames,
7+
Set<String> allModelClassAndPackageNames) {
8+
public JpaPersistenceUnitModel() {
9+
this(new TreeSet<>(), new TreeSet<>());
10+
}
11+
}

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -355,19 +355,18 @@ private static QuarkusPersistenceUnitDescriptorWithSupportedDBKind generateReact
355355
BuildProducer<HotDeploymentWatchedFileBuildItem> hotDeploymentWatchedFiles,
356356
List<DatabaseKindDialectBuildItem> dbKindDialectBuildItems, boolean enableDefaultPersistenceUnit) {
357357

358-
Map<String, Set<String>> modelClassesAndPackagesPerPersistencesUnits = HibernateOrmProcessor
359-
.getModelClassesAndPackagesPerPersistenceUnits(hibernateOrmConfig, additionalJpaModelBuildItems, jpaModel,
358+
var modelPerPersistencesUnit = HibernateOrmProcessor
359+
.getModelPerPersistenceUnit(hibernateOrmConfig, additionalJpaModelBuildItems, jpaModel,
360360
index.getIndex(),
361361
enableDefaultPersistenceUnit);
362362

363-
Set<String> modelClassesAndPackages = modelClassesAndPackagesPerPersistencesUnits
364-
.getOrDefault(persistenceUnitName, Collections.emptySet());
363+
var model = modelPerPersistencesUnit.get(persistenceUnitName);
365364

366365
QuarkusPersistenceUnitDescriptor descriptor = new QuarkusPersistenceUnitDescriptor(
367366
persistenceUnitName,
368367
new HibernateReactivePersistenceUnitProviderHelper(),
369368
PersistenceUnitTransactionType.RESOURCE_LOCAL,
370-
new ArrayList<>(modelClassesAndPackages),
369+
new ArrayList<>(model == null ? Collections.emptySet() : model.allModelClassAndPackageNames()),
371370
new Properties(),
372371
true);
373372

extensions/panache/hibernate-orm-panache/deployment/src/test/java/io/quarkus/hibernate/orm/panache/deployment/test/ErroneousConfigHotReloadTestCase.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@ public class ErroneousConfigHotReloadTestCase {
2121

2222
@Test
2323
public void test() {
24+
// Because there is no entity, the fact that there is no datasource will not trigger an error:
25+
// the persistence unit will simply be automatically deactivated.
26+
// Panache will however raise an exception on first attempt to use a class that is not, in fact, an entity.
27+
RestAssured.when().get("/unannotatedEntity").then().statusCode(500).body(containsString("@Entity"))
28+
.body(not(containsString("NullPointer")));
29+
30+
TEST.modifySourceFile(UnAnnotatedEntity.class, new Function<String, String>() {
31+
@Override
32+
public String apply(String s) {
33+
return s.replace("//", "");
34+
}
35+
});
36+
37+
// Once we do have entities, the persistence unit will be active,
38+
// but will fail to start since there is no datasource.
2439
RestAssured.when()
2540
.get("/unannotatedEntity").then().statusCode(500)
2641
// Weirdly, in case of build errors, Quarkus will return the error as HTML, even if we set the content type to JSON...
@@ -36,16 +51,6 @@ public String apply(String s) {
3651
}
3752
});
3853

39-
RestAssured.when().get("/unannotatedEntity").then().statusCode(500).body(containsString("@Entity"))
40-
.body(not(containsString("NullPointer")));
41-
42-
TEST.modifySourceFile(UnAnnotatedEntity.class, new Function<String, String>() {
43-
@Override
44-
public String apply(String s) {
45-
return s.replace("//", "");
46-
}
47-
});
48-
4954
RestAssured.when().get("/unannotatedEntity").then().statusCode(200);
5055
}
5156

0 commit comments

Comments
 (0)