Skip to content

Commit 2cf667c

Browse files
authored
Merge pull request quarkusio#50262 from yrodiere/i50247
Ignore implicit default persistence units if there is no datasource and no entity
2 parents 164ed14 + 67b7a1f commit 2cf667c

File tree

21 files changed

+436
-202
lines changed

21 files changed

+436
-202
lines changed

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

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -892,39 +892,53 @@ 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;
917-
throw PersistenceUnitUtil.unableToFindDataSource(persistenceUnitName, dataSourceName,
918-
DataSourceUtil.dataSourceNotConfigured(dataSourceName));
923+
var cause = DataSourceUtil.dataSourceNotConfigured(dataSourceName);
924+
throw new ConfigurationException(String.format(Locale.ROOT,
925+
"Persistence unit '%s' defines entities %s, but its datasource '%s' cannot be found: %s"
926+
+ " Alternatively, disable Hibernate ORM by setting '%s=false', and the entities will be ignored.",
927+
persistenceUnitName, modelForDefaultPersistenceUnit.entityClassNames(),
928+
dataSourceName,
929+
cause.getMessage(),
930+
HibernateOrmRuntimeConfig.extensionPropertyKey("enabled")),
931+
cause);
919932
}
920933

921934
for (Entry<String, HibernateOrmConfigPersistenceUnit> persistenceUnitEntry : hibernateOrmConfig.namedPersistenceUnits()
922935
.entrySet()) {
936+
var persistenceUnitName = persistenceUnitEntry.getKey();
937+
var model = modelPerPersistencesUnit.get(persistenceUnitEntry.getKey());
923938
producePersistenceUnitDescriptorFromConfig(
924-
hibernateOrmConfig, jpaModel, persistenceUnitEntry.getKey(), persistenceUnitEntry.getValue(),
925-
modelClassesAndPackagesPerPersistencesUnits.getOrDefault(persistenceUnitEntry.getKey(),
926-
Collections.emptySet()),
927-
jpaModel.getXmlMappings(persistenceUnitEntry.getKey()),
939+
hibernateOrmConfig, jpaModel, persistenceUnitName, persistenceUnitEntry.getValue(),
940+
model == null ? Collections.emptySet() : model.allModelClassAndPackageNames(),
941+
jpaModel.getXmlMappings(persistenceUnitName),
928942
jdbcDataSources, applicationArchivesBuildItem, launchMode, capabilities,
929943
systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors,
930944
reflectiveMethods, unremovableBeans, storageEngineCollector, dbKindMetadataBuildItems);
@@ -1136,10 +1150,10 @@ private void enhanceEntities(final JpaModelBuildItem jpaModel,
11361150
}
11371151
}
11381152

1139-
public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceUnits(HibernateOrmConfig hibernateOrmConfig,
1153+
public static Map<String, JpaPersistenceUnitModel> getModelPerPersistenceUnit(HibernateOrmConfig hibernateOrmConfig,
11401154
List<AdditionalJpaModelBuildItem> additionalJpaModelBuildItems, JpaModelBuildItem jpaModel,
11411155
IndexView index, boolean enableDefaultPersistenceUnit) {
1142-
Map<String, Set<String>> modelClassesAndPackagesPerPersistenceUnits = new HashMap<>();
1156+
Map<String, JpaPersistenceUnitModel> modelPerPersistenceUnit = new HashMap<>();
11431157

11441158
boolean hasPackagesInQuarkusConfig = hasPackagesInQuarkusConfig(hibernateOrmConfig);
11451159
Collection<AnnotationInstance> packageLevelPersistenceUnitAnnotations = getPackageLevelPersistenceUnitAnnotations(
@@ -1205,9 +1219,11 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12051219
// No .packages configuration, no package-level persistence unit annotations,
12061220
// and no named persistence units: all the entities will be associated with the default one
12071221
// so we don't need to split them
1208-
Set<String> allModelClassesAndPackages = new HashSet<>(jpaModel.getAllModelClassNames());
1209-
allModelClassesAndPackages.addAll(jpaModel.getAllModelPackageNames());
1210-
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);
12111227
}
12121228

12131229
Set<String> modelClassesWithPersistenceUnitAnnotations = new TreeSet<>();
@@ -1225,13 +1241,18 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12251241
for (Entry<String, Set<String>> packageRuleEntry : packageRules.entrySet()) {
12261242
if (modelClassName.startsWith(packageRuleEntry.getKey())) {
12271243
for (String persistenceUnitName : packageRuleEntry.getValue()) {
1228-
modelClassesAndPackagesPerPersistenceUnits.putIfAbsent(persistenceUnitName, new HashSet<>());
1229-
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);
12301251

12311252
// also add the hierarchy to the persistence unit
12321253
// we would need to add all the underlying model to it but adding the hierarchy
12331254
// is necessary for Panache as we need to add PanacheEntity to the PU
1234-
modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).addAll(relatedModelClassNames);
1255+
model.allModelClassAndPackageNames().addAll(relatedModelClassNames);
12351256
}
12361257
}
12371258
}
@@ -1247,8 +1268,13 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12471268
}
12481269
assignedModelClasses.add(className); // Even if persistenceUnits is empty, the class is still assigned (to nothing)
12491270
for (String persistenceUnitName : persistenceUnits) {
1250-
modelClassesAndPackagesPerPersistenceUnits.putIfAbsent(persistenceUnitName, new HashSet<>());
1251-
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);
12521278
}
12531279
}
12541280

@@ -1258,7 +1284,8 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12581284
String.join("\n\t- ", modelClassesWithPersistenceUnitAnnotations)));
12591285
}
12601286

1261-
assignedModelClasses.addAll(modelClassesAndPackagesPerPersistenceUnits.values().stream().flatMap(Set::stream).toList());
1287+
assignedModelClasses.addAll(modelPerPersistenceUnit.values().stream()
1288+
.map(JpaPersistenceUnitModel::allModelClassAndPackageNames).flatMap(Set::stream).toList());
12621289
Set<String> unaffectedModelClasses = jpaModel.getAllModelClassNames().stream()
12631290
.filter(c -> !assignedModelClasses.contains(c))
12641291
.collect(Collectors.toCollection(TreeSet::new));
@@ -1274,12 +1301,13 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
12741301
continue;
12751302
}
12761303
for (String persistenceUnitName : persistenceUnitNames) {
1277-
modelClassesAndPackagesPerPersistenceUnits.putIfAbsent(persistenceUnitName, new HashSet<>());
1278-
modelClassesAndPackagesPerPersistenceUnits.get(persistenceUnitName).add(modelPackageName);
1304+
var model = modelPerPersistenceUnit.computeIfAbsent(persistenceUnitName,
1305+
ignored -> new JpaPersistenceUnitModel());
1306+
model.allModelClassAndPackageNames().add(modelPackageName);
12791307
}
12801308
}
12811309

1282-
return modelClassesAndPackagesPerPersistenceUnits;
1310+
return modelPerPersistenceUnit;
12831311
}
12841312

12851313
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-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/NoConfigNoEntitiesTest.java

Lines changed: 0 additions & 53 deletions
This file was deleted.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.quarkus.hibernate.orm.config;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.assertj.core.api.Assertions;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.extension.RegisterExtension;
8+
9+
import io.quarkus.test.QuarkusUnitTest;
10+
11+
public class NoDatasourceTest {
12+
13+
@RegisterExtension
14+
static QuarkusUnitTest runner = new QuarkusUnitTest()
15+
.withApplicationRoot((jar) -> jar
16+
.addClass(MyEntity.class))
17+
// Ideally we would not add quarkus-jdbc-h2 to the classpath and there _really_ wouldn't be a datasource,
18+
// but that's inconvenient given our testing setup,
19+
// so we'll just disable the implicit datasource.
20+
.overrideConfigKey("quarkus.datasource.jdbc", "false")
21+
.assertException(t -> assertThat(t)
22+
.hasMessageContainingAll(
23+
"Persistence unit '<default>' defines entities [" + MyEntity.class.getName()
24+
+ "], but its datasource '<default>' cannot be found",
25+
"Datasource '<default>' is not configured.",
26+
"To solve this, configure datasource '<default>'",
27+
"Refer to https://quarkus.io/guides/datasource for guidance.",
28+
"Alternatively, disable Hibernate ORM by setting 'quarkus.hibernate-orm.enabled=false', and the entities will be ignored"));
29+
30+
@Test
31+
public void test() {
32+
Assertions.fail("Startup should have failed");
33+
}
34+
35+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.quarkus.hibernate.orm.config;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import jakarta.enterprise.context.control.ActivateRequestContext;
6+
import jakarta.inject.Inject;
7+
8+
import org.hibernate.Session;
9+
import org.junit.jupiter.api.Test;
10+
import org.junit.jupiter.api.extension.RegisterExtension;
11+
12+
import io.quarkus.arc.InjectableInstance;
13+
import io.quarkus.test.QuarkusUnitTest;
14+
15+
public class NoEntitiesNoDatasourceTest {
16+
17+
@RegisterExtension
18+
static QuarkusUnitTest runner = new QuarkusUnitTest()
19+
.withEmptyApplication()
20+
// Ideally we would not add quarkus-jdbc-h2 to the classpath and there _really_ wouldn't be a datasource,
21+
// but that's inconvenient given our testing setup,
22+
// so we'll just disable the implicit datasource.
23+
.overrideConfigKey("quarkus.datasource.jdbc", "false");
24+
25+
@Inject
26+
InjectableInstance<Session> session;
27+
28+
// When having no entities, no configuration, and no datasource,
29+
// as long as the Hibernate ORM beans are not injected anywhere,
30+
// we should still be able to start the application.
31+
@Test
32+
@ActivateRequestContext
33+
public void test() {
34+
// But Hibernate ORM should be disabled, so its beans should not be there.
35+
assertThat(session.isUnsatisfied()).isTrue();
36+
}
37+
38+
}

extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/NoEntitiesTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616
/**
1717
* Test that a persistence unit without any entities does get started,
18-
* and can be used, be it only for native queries.
18+
* and can be used, be it only for native queries,
19+
* as long as a datasource is present.
1920
*/
2021
public class NoEntitiesTest {
2122

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

0 commit comments

Comments
 (0)