Skip to content

Use Arc features in Hibernate extensions for eager startup and active/inactive #48783

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.quarkus.hibernate.orm.deployment;

import static io.quarkus.hibernate.orm.deployment.util.HibernateProcessorUtil.hasEntities;
import static org.apache.commons.lang3.BooleanUtils.isFalse;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
Expand All @@ -10,7 +9,7 @@
import java.util.Locale;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import jakarta.enterprise.context.ApplicationScoped;
Expand Down Expand Up @@ -39,6 +38,7 @@
import org.objectweb.asm.ClassVisitor;

import io.quarkus.agroal.spi.JdbcDataSourceBuildItem;
import io.quarkus.arc.ActiveResult;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
import io.quarkus.arc.deployment.BeanDefiningAnnotationBuildItem;
Expand Down Expand Up @@ -154,7 +154,6 @@ public void transform(TransformationContext transformationContext) {
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void generateJpaConfigBean(HibernateOrmRecorder recorder,
Capabilities capabilities,
BuildProducer<SyntheticBeanBuildItem> syntheticBeanBuildItemBuildProducer) {
ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem
.configure(JPAConfig.class)
Expand All @@ -180,21 +179,22 @@ void generateHibernateBeans(HibernateOrmRecorder recorder,
return;
}

Function<String, AnnotationInstance> createPersistenceUnitQualifier = (puName) -> AnnotationInstance
.builder(PersistenceUnit.class).add("value", puName).build();
AnnotationInstance defaultQualifierInstance = AnnotationInstance.builder(Default.class).build();

// we have only one persistence unit defined in a persistence.xml: we make it the default even if it has a name
// NOTE: In this case we know we're not using Hibernate Reactive, because it doesn't support persistence.xml.
if (persistenceUnitDescriptors.size() == 1 && persistenceUnitDescriptors.get(0).isFromPersistenceXml()) {
String persistenceUnitName = persistenceUnitDescriptors.get(0).getPersistenceUnitName();
var persistenceUnitDescriptor = persistenceUnitDescriptors.get(0);
PersistenceUnitReference puRef = new PersistenceUnitReference(
persistenceUnitDescriptor.getPersistenceUnitName(),
true, recorder.checkActiveSupplier(
persistenceUnitDescriptor.getPersistenceUnitName(),
persistenceUnitDescriptor.getConfig().getDataSource(),
persistenceUnitDescriptor.isFromPersistenceXml()));

produceSessionFactoryBean(syntheticBeanBuildItemBuildProducer, recorder, persistenceUnitName, true, true);
produceSessionFactoryBean(syntheticBeanBuildItemBuildProducer, recorder, puRef);

produceSessionBeans(syntheticBeanBuildItemBuildProducer, recorder, persistenceUnitName, true, true);
produceSessionBeans(syntheticBeanBuildItemBuildProducer, recorder, puRef);

produceFactoryDependentBeans(syntheticBeanBuildItemBuildProducer, recorder, persistenceUnitName,
true, true, defaultQualifierInstance);
produceFactoryDependentBeans(syntheticBeanBuildItemBuildProducer, recorder, puRef);

return;
}
Expand All @@ -208,27 +208,19 @@ void generateHibernateBeans(HibernateOrmRecorder recorder,
}

String persistenceUnitName = persistenceUnitDescriptor.getPersistenceUnitName();
// Hibernate Reactive does not use the same name for its default persistence unit,
// but we still want to use the @Default qualifier for that PU.
// We will need to fix this at some point, see https://github.com/quarkusio/quarkus/issues/21110
String persistenceUnitConfigName = persistenceUnitDescriptor.getConfigurationName();
boolean isDefaultPU = PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitConfigName);
boolean isNamedPU = isFalse(isDefaultPU);
AnnotationInstance sessionFactoryQualifier;
if (isDefaultPU) {
sessionFactoryQualifier = defaultQualifierInstance;
} else {
sessionFactoryQualifier = createPersistenceUnitQualifier.apply(persistenceUnitName);
}

produceSessionFactoryBean(syntheticBeanBuildItemBuildProducer, recorder, persistenceUnitName, isDefaultPU,
isNamedPU);
PersistenceUnitReference puRef = new PersistenceUnitReference(
persistenceUnitName,
PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitName),
recorder.checkActiveSupplier(
persistenceUnitDescriptor.getPersistenceUnitName(),
persistenceUnitDescriptor.getConfig().getDataSource(),
persistenceUnitDescriptor.isFromPersistenceXml()));

produceSessionBeans(syntheticBeanBuildItemBuildProducer, recorder, persistenceUnitName, isDefaultPU, isNamedPU);
produceSessionFactoryBean(syntheticBeanBuildItemBuildProducer, recorder, puRef);

produceFactoryDependentBeans(syntheticBeanBuildItemBuildProducer, recorder, persistenceUnitName,
isDefaultPU, isNamedPU, sessionFactoryQualifier);
produceSessionBeans(syntheticBeanBuildItemBuildProducer, recorder, puRef);

produceFactoryDependentBeans(syntheticBeanBuildItemBuildProducer, recorder, puRef);
}
}

Expand Down Expand Up @@ -354,16 +346,19 @@ void validatePersistenceUnitExtensions(ValidationPhaseBuildItem validationPhase,
}
}

private static <T> ExtendedBeanConfigurator createSyntheticBean(String persistenceUnitName,
boolean isDefaultPersistenceUnit, boolean isNamedPersistenceUnit,
private static <T> ExtendedBeanConfigurator createSyntheticBean(PersistenceUnitReference puRef,
Class<T> type, List<DotName> allExposedTypes, boolean defaultBean) {
ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem
.configure(type)
// NOTE: this is using ApplicationScope and not Singleton, by design, in order to be mockable
// See https://github.com/quarkusio/quarkus/issues/16437
.scope(ApplicationScoped.class)
.unremovable()
.setRuntimeInit();
.setRuntimeInit()
// FIXME wouldn't this cause persistence units to be initialized one by one?
// FIXME we should fail on startup even if Panache is the only consumer and the datasource does not set the URL -- currently we don't
.startup()
.checkActive(puRef.checkActiveSupplier);

for (DotName exposedType : allExposedTypes) {
configurator.addType(exposedType);
Expand All @@ -373,12 +368,11 @@ private static <T> ExtendedBeanConfigurator createSyntheticBean(String persisten
configurator.defaultBean();
}

if (isDefaultPersistenceUnit) {
if (puRef.isDefaultPU) {
configurator.addQualifier(Default.class);
}
if (isNamedPersistenceUnit) {
configurator.addQualifier().annotation(DotNames.NAMED).addValue("value", persistenceUnitName).done();
configurator.addQualifier().annotation(PersistenceUnit.class).addValue("value", persistenceUnitName).done();
} else {
configurator.addQualifier().annotation(DotNames.NAMED).addValue("value", puRef.persistenceUnitName).done();
configurator.addQualifier().annotation(PersistenceUnit.class).addValue("value", puRef.persistenceUnitName).done();
}

return configurator;
Expand All @@ -387,94 +381,91 @@ private static <T> ExtendedBeanConfigurator createSyntheticBean(String persisten
private void produceSessionBeans(
BuildProducer<SyntheticBeanBuildItem> producer,
HibernateOrmRecorder recorder,
String persistenceUnitName,
boolean isDefaultPU,
boolean isNamedPU) {
PersistenceUnitReference puRef) {

// Create Session bean
producer.produce(createSyntheticBean(persistenceUnitName,
isDefaultPU, isNamedPU,
producer.produce(createSyntheticBean(puRef,
Session.class, SESSION_EXPOSED_TYPES, false)
.createWith(recorder.sessionSupplier(persistenceUnitName))
.createWith(recorder.sessionSupplier(puRef.persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class)))
.done());

// Create StatelessSession bean
producer.produce(createSyntheticBean(persistenceUnitName,
isDefaultPU, isNamedPU,
producer.produce(createSyntheticBean(puRef,
StatelessSession.class, STATELESS_SESSION_EXPOSED_TYPES, false)
.createWith(recorder.statelessSessionSupplier(persistenceUnitName))
.createWith(recorder.statelessSessionSupplier(puRef.persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class)))
.done());
}

private void produceSessionFactoryBean(
BuildProducer<SyntheticBeanBuildItem> producer,
HibernateOrmRecorder recorder,
String persistenceUnitName,
boolean isDefaultPU,
boolean isNamedPU) {
PersistenceUnitReference puRef) {

producer.produce(createSyntheticBean(persistenceUnitName,
isDefaultPU, isNamedPU,
producer.produce(createSyntheticBean(puRef,
SessionFactory.class, SESSION_FACTORY_EXPOSED_TYPES, true)
.createWith(recorder.sessionFactorySupplier(persistenceUnitName))
.createWith(recorder.sessionFactorySupplier(puRef.persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(JPAConfig.class)))
.done());
}

private void produceFactoryDependentBeans(
BuildProducer<SyntheticBeanBuildItem> producer,
HibernateOrmRecorder recorder,
String persistenceUnitName,
boolean isDefaultPU,
boolean isNamedPU,
AnnotationInstance sessionFactoryQualifier) {
PersistenceUnitReference puRef) {
AnnotationInstance sessionFactoryQualifier;
if (puRef.isDefaultPU) {
sessionFactoryQualifier = AnnotationInstance.builder(Default.class).build();
} else {
sessionFactoryQualifier = AnnotationInstance
.builder(PersistenceUnit.class).add("value", puRef.persistenceUnitName).build();
}

// Create CriteriaBuilder bean
producer.produce(createSyntheticBean(persistenceUnitName,
isDefaultPU, isNamedPU,
producer.produce(createSyntheticBean(puRef,
HibernateCriteriaBuilder.class, CRITERIA_BUILDER_EXPOSED_TYPES, false)
.createWith(recorder.criteriaBuilderSupplier(persistenceUnitName))
.createWith(recorder.criteriaBuilderSupplier(puRef.persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(SessionFactory.class)),
sessionFactoryQualifier)
.done());

// Create Metamodel bean
producer.produce(createSyntheticBean(persistenceUnitName,
isDefaultPU, isNamedPU,
producer.produce(createSyntheticBean(puRef,
jakarta.persistence.metamodel.Metamodel.class, METAMODEL_EXPOSED_TYPES, false)
.createWith(recorder.metamodelSupplier(persistenceUnitName))
.createWith(recorder.metamodelSupplier(puRef.persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(SessionFactory.class)),
sessionFactoryQualifier)
.done());

// Create SchemaManager bean
producer.produce(createSyntheticBean(persistenceUnitName,
isDefaultPU, isNamedPU,
producer.produce(createSyntheticBean(puRef,
SchemaManager.class, SCHEMA_MANAGER_EXPOSED_TYPES, false)
.createWith(recorder.schemaManagerSupplier(persistenceUnitName))
.createWith(recorder.schemaManagerSupplier(puRef.persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(SessionFactory.class)),
sessionFactoryQualifier)
.done());

// Create Cache bean
producer.produce(createSyntheticBean(persistenceUnitName,
isDefaultPU, isNamedPU,
producer.produce(createSyntheticBean(puRef,
org.hibernate.Cache.class, CACHE_EXPOSED_TYPES, false)
.createWith(recorder.cacheSupplier(persistenceUnitName))
.createWith(recorder.cacheSupplier(puRef.persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(SessionFactory.class)),
sessionFactoryQualifier)
.done());

// Create PersistenceUnitUtil bean
producer.produce(createSyntheticBean(persistenceUnitName,
isDefaultPU, isNamedPU,
producer.produce(createSyntheticBean(puRef,
jakarta.persistence.PersistenceUnitUtil.class, PERSISTENCE_UNIT_UTIL_EXPOSED_TYPES, false)
.createWith(recorder.persistenceUnitUtilSupplier(persistenceUnitName))
.createWith(recorder.persistenceUnitUtilSupplier(puRef.persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(SessionFactory.class)),
sessionFactoryQualifier)
.done());
}

private record PersistenceUnitReference(
String persistenceUnitName,
boolean isDefaultPU,
Supplier<ActiveResult> checkActiveSupplier) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem;
import io.quarkus.hibernate.orm.deployment.spi.AdditionalJpaModelBuildItem;
import io.quarkus.hibernate.orm.deployment.spi.DatabaseKindDialectBuildItem;
import io.quarkus.hibernate.orm.runtime.HibernateOrmPersistenceUnitProviderHelper;
import io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder;
import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig;
import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil;
Expand Down Expand Up @@ -291,7 +292,9 @@ public ImpliedBlockingPersistenceUnitTypeBuildItem defineTypeOfImpliedPU(
}

@BuildStep
@Record(RUNTIME_INIT)
public void configurationDescriptorBuilding(
HibernateOrmRecorder recorder,
HibernateOrmConfig hibernateOrmConfig,
CombinedIndexBuildItem index,
ImpliedBlockingPersistenceUnitTypeBuildItem impliedPU,
Expand Down Expand Up @@ -920,13 +923,15 @@ private static void producePersistenceUnitDescriptorFromConfig(
List<DatabaseKindDialectBuildItem> dbKindMetadataBuildItems) {
Optional<JdbcDataSourceBuildItem> jdbcDataSource = findJdbcDataSource(persistenceUnitName, persistenceUnitConfig,
jdbcDataSources);
Optional<String> dataSourceName = jdbcDataSource.map(JdbcDataSourceBuildItem::getName);

if (modelClassesAndPackages.isEmpty()) {
LOG.warnf("Could not find any entities affected to the persistence unit '%s'.", persistenceUnitName);
}

QuarkusPersistenceUnitDescriptor descriptor = new QuarkusPersistenceUnitDescriptor(
persistenceUnitName, persistenceUnitName,
new HibernateOrmPersistenceUnitProviderHelper(),
PersistenceUnitTransactionType.JTA,
// That's right, we're pushing both class names and package names
// to a method called "addClasses".
Expand Down Expand Up @@ -962,7 +967,7 @@ private static void producePersistenceUnitDescriptorFromConfig(
persistenceUnitDescriptors.produce(
new PersistenceUnitDescriptorBuildItem(descriptor,
new RecordedConfig(
jdbcDataSource.map(JdbcDataSourceBuildItem::getName),
dataSourceName,
jdbcDataSource.map(JdbcDataSourceBuildItem::getDbKind),
jdbcDataSource.flatMap(JdbcDataSourceBuildItem::getDbVersion),
persistenceUnitConfig.dialect().dialect(),
Expand Down
Loading