diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java index 5a26a6a1f33a5..d934ebd9e4154 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java @@ -471,19 +471,22 @@ public BytecodeRecorderConstantDefinitionBuildItem pregenProxies( LiveReloadBuildItem liveReloadBuildItem, ExecutorService buildExecutor) throws ExecutionException, InterruptedException { Set managedClassAndPackageNames = new HashSet<>(jpaModel.getEntityClassNames()); + Set managedClassNamesOnly = new HashSet<>(jpaModel.getEntityClassNames()); for (PersistenceUnitDescriptorBuildItem pud : persistenceUnitDescriptorBuildItems) { // Note: getManagedClassNames() can also return *package* names // See the source code of Hibernate ORM for proof: // org.hibernate.boot.archive.scan.internal.ScanResultCollector.isListedOrDetectable // is used for packages too, and it relies (indirectly) on getManagedClassNames(). managedClassAndPackageNames.addAll(pud.getManagedClassNames()); + managedClassNamesOnly.addAll(pud.getManagedClassNamesOnly()); } for (AdditionalJpaModelBuildItem additionalJpaModelBuildItem : additionalJpaModelBuildItems) { managedClassAndPackageNames.add(additionalJpaModelBuildItem.getClassName()); + managedClassNamesOnly.add(additionalJpaModelBuildItem.getClassName()); } - PreGeneratedProxies proxyDefinitions = generateProxies(managedClassAndPackageNames, + PreGeneratedProxies proxyDefinitions = generateProxies(managedClassAndPackageNames, managedClassNamesOnly, indexBuildItem.getIndex(), transformedClassesBuildItem, generatedClassBuildItemBuildProducer, liveReloadBuildItem, buildExecutor); @@ -906,6 +909,7 @@ private void handleHibernateORMWithNoPersistenceXml( hibernateOrmConfig, jpaModel, PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, hibernateOrmConfig.defaultPersistenceUnit(), modelForDefaultPersistenceUnit.allModelClassAndPackageNames(), + modelForDefaultPersistenceUnit.allModelClassNamesOnly(), jpaModel.getXmlMappings(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME), jdbcDataSources, applicationArchivesBuildItem, launchMode, capabilities, systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors, @@ -938,6 +942,7 @@ private void handleHibernateORMWithNoPersistenceXml( producePersistenceUnitDescriptorFromConfig( hibernateOrmConfig, jpaModel, persistenceUnitName, persistenceUnitEntry.getValue(), model == null ? Collections.emptySet() : model.allModelClassAndPackageNames(), + model == null ? Collections.emptySet() : model.allModelClassNamesOnly(), jpaModel.getXmlMappings(persistenceUnitName), jdbcDataSources, applicationArchivesBuildItem, launchMode, capabilities, systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors, @@ -955,6 +960,7 @@ private static void producePersistenceUnitDescriptorFromConfig( String persistenceUnitName, HibernateOrmConfigPersistenceUnit persistenceUnitConfig, Set modelClassesAndPackages, + Set allModelClassNamesOnly, List xmlMappings, List jdbcDataSources, ApplicationArchivesBuildItem applicationArchivesBuildItem, @@ -987,6 +993,7 @@ private static void producePersistenceUnitDescriptorFromConfig( // - the comment at org/hibernate/boot/model/process/internal/ScanningCoordinator.java:246: // "IMPL NOTE : "explicitlyListedClassNames" can contain class or package names..." new ArrayList<>(modelClassesAndPackages), + new ArrayList<>(allModelClassNamesOnly), new Properties(), false); Set entityClassNames = new HashSet<>(descriptor.getManagedClassNames()); @@ -1155,6 +1162,11 @@ public static Map getModelPerPersistenceUnit(Hi IndexView index, boolean enableDefaultPersistenceUnit) { Map modelPerPersistenceUnit = new HashMap<>(); + Set managedClassNamesOnly = new HashSet<>(jpaModel.getManagedClassNames()); + for (AdditionalJpaModelBuildItem additionalJpaModel : additionalJpaModelBuildItems) { + managedClassNamesOnly.add(additionalJpaModel.getClassName()); + } + boolean hasPackagesInQuarkusConfig = hasPackagesInQuarkusConfig(hibernateOrmConfig); Collection packageLevelPersistenceUnitAnnotations = getPackageLevelPersistenceUnitAnnotations( index); @@ -1223,19 +1235,26 @@ public static Map getModelPerPersistenceUnit(Hi model.entityClassNames().addAll(jpaModel.getEntityClassNames()); model.allModelClassAndPackageNames().addAll(jpaModel.getAllModelClassNames()); model.allModelClassAndPackageNames().addAll(jpaModel.getAllModelPackageNames()); + model.allModelClassNamesOnly().addAll(jpaModel.getAllModelClassNames()); return Map.of(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, model); } Set modelClassesWithPersistenceUnitAnnotations = new TreeSet<>(); for (String modelClassName : jpaModel.getAllModelClassNames()) { - ClassInfo modelClassInfo = index.getClassByName(DotName.createSimple(modelClassName)); - Set relatedModelClassNames = getRelatedModelClassNames(index, jpaModel.getAllModelClassNames(), - modelClassInfo); - if (modelClassInfo != null && (modelClassInfo.declaredAnnotation(ClassNames.QUARKUS_PERSISTENCE_UNIT) != null - || modelClassInfo.declaredAnnotation(ClassNames.QUARKUS_PERSISTENCE_UNIT_REPEATABLE_CONTAINER) != null)) { - modelClassesWithPersistenceUnitAnnotations.add(modelClassInfo.name().toString()); + Set relatedModelClassNames = new HashSet<>(); + if (managedClassNamesOnly.contains(modelClassName)) { + + ClassInfo modelClassInfo = index.getClassByName(DotName.createSimple(modelClassName)); + relatedModelClassNames = getRelatedModelClassNames(index, jpaModel.getAllModelClassNames(), + modelClassInfo); + + if (modelClassInfo != null && (modelClassInfo.declaredAnnotation(ClassNames.QUARKUS_PERSISTENCE_UNIT) != null + || modelClassInfo + .declaredAnnotation(ClassNames.QUARKUS_PERSISTENCE_UNIT_REPEATABLE_CONTAINER) != null)) { + modelClassesWithPersistenceUnitAnnotations.add(modelClassInfo.name().toString()); + } } for (Entry> packageRuleEntry : packageRules.entrySet()) { @@ -1246,6 +1265,7 @@ public static Map getModelPerPersistenceUnit(Hi if (jpaModel.getEntityClassNames().contains(modelClassName)) { model.entityClassNames().add(modelClassName); + model.allModelClassNamesOnly().add(modelClassName); } model.allModelClassAndPackageNames().add(modelClassName); @@ -1273,6 +1293,7 @@ public static Map getModelPerPersistenceUnit(Hi if (jpaModel.getEntityClassNames().contains(className)) { model.entityClassNames().add(className); + model.allModelClassNamesOnly().add(className); } model.allModelClassAndPackageNames().add(className); } @@ -1433,7 +1454,8 @@ private static MultiTenancyStrategy getMultiTenancyStrategy(Optional mul return multiTenancyStrategy; } - private PreGeneratedProxies generateProxies(Set managedClassAndPackageNames, IndexView combinedIndex, + private PreGeneratedProxies generateProxies(Set managedClassAndPackageNames, Set managedClassNamesOnly, + IndexView combinedIndex, TransformedClassesBuildItem transformedClassesBuildItem, BuildProducer generatedClassBuildItemBuildProducer, LiveReloadBuildItem liveReloadBuildItem, @@ -1460,18 +1482,19 @@ private PreGeneratedProxies generateProxies(Set managedClassAndPackageNa final ConcurrentLinkedDeque> generatedProxyQueue = new ConcurrentLinkedDeque<>(); Set proxyInterfaceNames = Set.of(ClassNames.HIBERNATE_PROXY.toString()); - for (String managedClassOrPackageName : managedClassAndPackageNames) { - if (proxyCache.cache.containsKey(managedClassOrPackageName) - && !isModified(managedClassOrPackageName, changedClasses, combinedIndex)) { - CachedProxy proxy = proxyCache.cache.get(managedClassOrPackageName); + for (String managedClassName : managedClassNamesOnly) { + + if (proxyCache.cache.containsKey(managedClassName) + && !isModified(managedClassName, changedClasses, combinedIndex)) { + CachedProxy proxy = proxyCache.cache.get(managedClassName); generatedProxyQueue.add(CompletableFuture.completedFuture(proxy)); } else { - if (!proxyHelper.isProxiable(combinedIndex.getClassByName(managedClassOrPackageName))) { + if (!proxyHelper.isProxiable(combinedIndex.getClassByName(managedClassName))) { // we need to make sure we have a class and not a package and that it is proxiable continue; } // we now are sure we have a proper class and not a package, let's avoid the confusion - String managedClass = managedClassOrPackageName; + String managedClass = managedClassName; generatedProxyQueue.add(buildExecutor.submit(() -> { DynamicType.Unloaded unloaded = proxyHelper.buildUnloadedProxy(managedClass); return new CachedProxy(managedClass, unloaded); diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaPersistenceUnitModel.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaPersistenceUnitModel.java index 96e7016bd826f..8638557587f2a 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaPersistenceUnitModel.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaPersistenceUnitModel.java @@ -4,8 +4,8 @@ import java.util.TreeSet; public record JpaPersistenceUnitModel(Set entityClassNames, - Set allModelClassAndPackageNames) { + Set allModelClassAndPackageNames, Set allModelClassNamesOnly) { public JpaPersistenceUnitModel() { - this(new TreeSet<>(), new TreeSet<>()); + this(new TreeSet<>(), new TreeSet<>(), new TreeSet<>()); } } diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java index 78024060b8f8b..524a7ee668657 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java @@ -50,6 +50,10 @@ public Collection getManagedClassNames() { return descriptor.getManagedClassNames(); } + public Collection getManagedClassNamesOnly() { + return descriptor.getManagedClassNamesOnly(); + } + public String getExplicitSqlImportScriptResourceName() { return descriptor.getProperties().getProperty("jakarta.persistence.sql-load-script-source"); } diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/PackageInfoNonRegressionTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/PackageInfoNonRegressionTest.java new file mode 100644 index 0000000000000..6edb4be6b7d6d --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/packages/PackageInfoNonRegressionTest.java @@ -0,0 +1,52 @@ +package io.quarkus.hibernate.orm.packages; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.logging.Level; + +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.transaction.Transactional; +import jakarta.transaction.UserTransaction; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.hibernate.orm.TransactionTestUtils; +import io.quarkus.test.QuarkusUnitTest; + +public class PackageInfoNonRegressionTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClass(TransactionTestUtils.class) + .addPackage(PackageInfoNonRegressionTest.class.getPackage()) + .addAsResource("application.properties")) + .setLogRecordPredicate(record -> record.getLevel().intValue() > Level.WARNING.intValue()) + .assertLogRecords(logs -> assertThat(logs).noneMatch( + log -> log.getMessage().contains("ClassLoader QuarkusClassLoader"))); + + @Inject + EntityManager entityManager; + + @Inject + UserTransaction transaction; + + @Test + @Transactional + public void test() { + + ParentEntity parent1 = new ParentEntity("parent1"); + entityManager.persist(parent1); + + ParentEntity parent2 = new ParentEntity("parent2"); + entityManager.persist(parent2); + + List entities = entityManager.createQuery("from ParentEntity ", ParentEntity.class).getResultList(); + assertEquals(2, entities.size()); + + } +} \ No newline at end of file diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDescriptor.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDescriptor.java index bababca0557be..0b6a0125e6480 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDescriptor.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDescriptor.java @@ -29,13 +29,14 @@ public final class QuarkusPersistenceUnitDescriptor implements PersistenceUnitDe private final ValidationMode validationMode; private final SharedCacheMode sharedCacheMode; private final List managedClassNames; + private final List managedClassNamesOnly; private final Properties properties; private final boolean reactive; public QuarkusPersistenceUnitDescriptor(String name, QuarkusPersistenceUnitProviderHelper providerHelper, PersistenceUnitTransactionType persistenceUnitTransactionType, - List managedClassNames, + List managedClassNames, List managedClassNamesOnly, Properties properties, boolean reactive) { this.name = name; this.providerHelper = providerHelper; @@ -45,6 +46,7 @@ public QuarkusPersistenceUnitDescriptor(String name, this.validationMode = null; this.sharedCacheMode = null; this.managedClassNames = managedClassNames; + this.managedClassNamesOnly = managedClassNamesOnly; this.properties = properties; this.reactive = reactive; } @@ -60,7 +62,8 @@ public QuarkusPersistenceUnitDescriptor(String name, QuarkusPersistenceUnitProviderHelper providerHelper, String providerClassName, boolean useQuotedIdentifiers, PersistenceUnitTransactionType persistenceUnitTransactionType, - ValidationMode validationMode, SharedCacheMode sharedCacheMode, List managedClassNames, + ValidationMode validationMode, SharedCacheMode sharedCacheMode, + List managedClassNames, List managedClassNamesOnly, Properties properties, boolean reactive) { this.name = name; this.providerHelper = providerHelper; @@ -70,6 +73,7 @@ public QuarkusPersistenceUnitDescriptor(String name, this.validationMode = validationMode; this.sharedCacheMode = sharedCacheMode; this.managedClassNames = managedClassNames; + this.managedClassNamesOnly = managedClassNamesOnly; this.properties = properties; this.reactive = reactive; } @@ -94,6 +98,7 @@ public static QuarkusPersistenceUnitDescriptor validateAndReadFrom(PersistenceUn toClone.getProviderClassName(), toClone.isUseQuotedIdentifiers(), toClone.getPersistenceUnitTransactionType(), toClone.getValidationMode(), toClone.getSharedCacheMode(), + Collections.unmodifiableList(toClone.getManagedClassNames()), Collections.unmodifiableList(toClone.getManagedClassNames()), toClone.getProperties(), false); } @@ -154,6 +159,10 @@ public List getManagedClassNames() { return managedClassNames; } + public List getManagedClassNamesOnly() { + return managedClassNamesOnly; + } + @Override public List getMappingFileNames() { // Mapping files can safely be ignored, see verifyIgnoredFields(). diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java index f2651285a0dbe..c1e0f8854016d 100644 --- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java @@ -367,6 +367,7 @@ private static QuarkusPersistenceUnitDescriptorWithSupportedDBKind generateReact new HibernateReactivePersistenceUnitProviderHelper(), PersistenceUnitTransactionType.RESOURCE_LOCAL, new ArrayList<>(model == null ? Collections.emptySet() : model.allModelClassAndPackageNames()), + new ArrayList<>(model == null ? Collections.emptySet() : model.allModelClassNamesOnly()), new Properties(), true); diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/deployment/HibernateSearchElasticsearchProcessor.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/deployment/HibernateSearchElasticsearchProcessor.java index ed5306bdb8b0a..eb6c9a7f47e2a 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/deployment/HibernateSearchElasticsearchProcessor.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/deployment/HibernateSearchElasticsearchProcessor.java @@ -85,7 +85,7 @@ public void build(HibernateSearchElasticsearchRecorder recorder, Collection indexedAnnotationsForPU = new ArrayList<>(); for (AnnotationInstance indexedAnnotation : indexedAnnotations) { String targetName = indexedAnnotation.target().asClass().name().toString(); - if (puDescriptor.getManagedClassNames().contains(targetName)) { + if (puDescriptor.getManagedClassesAndPackagedNames().contains(targetName)) { indexedAnnotationsForPU.add(indexedAnnotation); } }