diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java index 11ba38dedd6a8..914116d1f6e78 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.Locale; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import jakarta.enterprise.context.ApplicationScoped; @@ -14,6 +13,7 @@ import org.hibernate.SessionFactory; import org.jboss.jandex.AnnotationTarget.Kind; import org.jboss.jandex.AnnotationValue; +import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; import org.jboss.jandex.FieldInfo; import org.jboss.jandex.Type; @@ -23,10 +23,13 @@ import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; import io.quarkus.arc.deployment.BeanDefiningAnnotationBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem.ExtendedBeanConfigurator; import io.quarkus.arc.deployment.ValidationPhaseBuildItem; import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.DotNames; import io.quarkus.arc.processor.Transformation; +import io.quarkus.deployment.Capabilities; +import io.quarkus.deployment.Capability; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.BuildSteps; @@ -34,7 +37,9 @@ import io.quarkus.deployment.annotations.Record; import io.quarkus.hibernate.orm.PersistenceUnit; import io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder; +import io.quarkus.hibernate.orm.runtime.JPAConfig; import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; +import io.quarkus.hibernate.orm.runtime.TransactionSessions; @BuildSteps(onlyIf = HibernateOrmEnabled.class) public class HibernateOrmCdiProcessor { @@ -113,6 +118,7 @@ void generateDataSourceBeans(HibernateOrmRecorder recorder, List persistenceUnitDescriptors, ImpliedBlockingPersistenceUnitTypeBuildItem impliedBlockingPersistenceUnitType, List jdbcDataSources, // just make sure the datasources are initialized + Capabilities capabilities, BuildProducer additionalBeans, BuildProducer syntheticBeanBuildItemBuildProducer) { if (persistenceUnitDescriptors.isEmpty()) { @@ -127,17 +133,22 @@ void generateDataSourceBeans(HibernateOrmRecorder recorder, syntheticBeanBuildItemBuildProducer .produce(createSyntheticBean(persistenceUnitName, true, true, - SessionFactory.class, SESSION_FACTORY_EXPOSED_TYPES, - recorder.sessionFactorySupplier(persistenceUnitName), - true)); - - syntheticBeanBuildItemBuildProducer - .produce(createSyntheticBean(persistenceUnitName, - true, true, - Session.class, SESSION_EXPOSED_TYPES, - recorder.sessionSupplier(persistenceUnitName), - false)); - + SessionFactory.class, SESSION_FACTORY_EXPOSED_TYPES, true) + .createWith(recorder.sessionFactorySupplier(persistenceUnitName)) + .addInjectionPoint(ClassType.create(DotName.createSimple(JPAConfig.class))) + .done()); + + if (capabilities.isPresent(Capability.TRANSACTIONS)) { + // Do register a Session/EntityManager bean only if JTA is available + // Note that the Hibernate Reactive extension excludes JTA intentionally + syntheticBeanBuildItemBuildProducer + .produce(createSyntheticBean(persistenceUnitName, + true, true, + Session.class, SESSION_EXPOSED_TYPES, false) + .createWith(recorder.sessionSupplier(persistenceUnitName)) + .addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class))) + .done()); + } return; } @@ -153,16 +164,22 @@ void generateDataSourceBeans(HibernateOrmRecorder recorder, syntheticBeanBuildItemBuildProducer .produce(createSyntheticBean(persistenceUnitName, isDefaultPU, isNamedPU, - SessionFactory.class, SESSION_FACTORY_EXPOSED_TYPES, - recorder.sessionFactorySupplier(persistenceUnitName), - true)); - - syntheticBeanBuildItemBuildProducer - .produce(createSyntheticBean(persistenceUnitName, - isDefaultPU, isNamedPU, - Session.class, SESSION_EXPOSED_TYPES, - recorder.sessionSupplier(persistenceUnitName), - false)); + SessionFactory.class, SESSION_FACTORY_EXPOSED_TYPES, true) + .createWith(recorder.sessionFactorySupplier(persistenceUnitName)) + .addInjectionPoint(ClassType.create(DotName.createSimple(JPAConfig.class))) + .done()); + + if (capabilities.isPresent(Capability.TRANSACTIONS)) { + // Do register a Session/EntityManager bean only if JTA is available + // Note that the Hibernate Reactive extension excludes JTA intentionally + syntheticBeanBuildItemBuildProducer + .produce(createSyntheticBean(persistenceUnitName, + isDefaultPU, isNamedPU, + Session.class, SESSION_EXPOSED_TYPES, false) + .createWith(recorder.sessionSupplier(persistenceUnitName)) + .addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class))) + .done()); + } } } @@ -200,17 +217,16 @@ void validatePersistenceUnitExtensions(ValidationPhaseBuildItem validationPhase, } } - private static SyntheticBeanBuildItem createSyntheticBean(String persistenceUnitName, + private static ExtendedBeanConfigurator createSyntheticBean(String persistenceUnitName, boolean isDefaultPersistenceUnit, boolean isNamedPersistenceUnit, - Class type, List allExposedTypes, Supplier supplier, boolean defaultBean) { - SyntheticBeanBuildItem.ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem + Class type, List 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() - .supplier(supplier); + .setRuntimeInit(); for (DotName exposedType : allExposedTypes) { configurator.addType(exposedType); @@ -228,6 +244,6 @@ private static SyntheticBeanBuildItem createSyntheticBean(String persistence configurator.addQualifier().annotation(PersistenceUnit.class).addValue("value", persistenceUnitName).done(); } - return configurator.done(); + return configurator; } } diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigActiveFalseAndEntityTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigActiveFalseAndEntityTest.java index f2e74f8c0d424..bc69b400a750a 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigActiveFalseAndEntityTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigActiveFalseAndEntityTest.java @@ -70,6 +70,8 @@ public void entityManager() { assertThat(entityManager).isNotNull(); // However, any attempt to use it at runtime will fail. assertThatThrownBy(() -> entityManager.find(MyEntity.class, 0L)) + // Note that unlike for EntityManagerFactory/SessionFactory we get an IllegalStateException because + // the real Session/EntityManager instance is created lazily through SessionLazyDelegator in HibernateOrmRecorder.sessionSupplier() .isInstanceOf(IllegalStateException.class) .hasMessageContainingAll( "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit ", @@ -87,6 +89,8 @@ public void session() { assertThat(session).isNotNull(); // However, any attempt to use it at runtime will fail. assertThatThrownBy(() -> session.find(MyEntity.class, 0L)) + // Note that unlike for EntityManagerFactory/SessionFactory we get an IllegalStateException because + // the real Session/EntityManager instance is created lazily through SessionLazyDelegator in HibernateOrmRecorder.sessionSupplier() .isInstanceOf(IllegalStateException.class) .hasMessageContainingAll( "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit ", diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java index 3eb01167946f9..756dfa8664bd9 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import jakarta.inject.Inject; @@ -18,7 +19,7 @@ import org.hibernate.integrator.spi.Integrator; import org.jboss.logging.Logger; -import io.quarkus.arc.Arc; +import io.quarkus.arc.SyntheticCreationalContext; import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.arc.runtime.BeanContainerListener; import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDefinition; @@ -95,11 +96,12 @@ public void startAllPersistenceUnits(BeanContainer beanContainer) { beanContainer.beanInstance(JPAConfig.class).startAll(); } - public Supplier sessionFactorySupplier(String persistenceUnitName) { - return new Supplier() { + public Function, SessionFactory> sessionFactorySupplier( + String persistenceUnitName) { + return new Function, SessionFactory>() { @Override - public SessionFactory get() { - SessionFactory sessionFactory = Arc.container().instance(JPAConfig.class).get() + public SessionFactory apply(SyntheticCreationalContext context) { + SessionFactory sessionFactory = context.getInjectedReference(JPAConfig.class) .getEntityManagerFactory(persistenceUnitName) .unwrap(SessionFactory.class); @@ -108,12 +110,12 @@ public SessionFactory get() { }; } - public Supplier sessionSupplier(String persistenceUnitName) { - return new Supplier() { + public Function, Session> sessionSupplier(String persistenceUnitName) { + return new Function, Session>() { + @Override - public Session get() { - TransactionSessions transactionSessions = Arc.container() - .instance(TransactionSessions.class).get(); + public Session apply(SyntheticCreationalContext context) { + TransactionSessions transactionSessions = context.getInjectedReference(TransactionSessions.class); return new SessionLazyDelegator(new Supplier() { @Override public Session get() { diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigActiveFalseAndEntityTest.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigActiveFalseAndEntityTest.java index 5a4073de0baa8..5611049700705 100644 --- a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigActiveFalseAndEntityTest.java +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigActiveFalseAndEntityTest.java @@ -2,7 +2,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertThrows; +import jakarta.enterprise.inject.CreationException; import jakarta.persistence.EntityManagerFactory; import org.hibernate.SessionFactory; @@ -30,7 +32,8 @@ public void entityManagerFactory() { // So the bean cannot be null. assertThat(entityManagerFactory).isNotNull(); // However, any attempt to use it at runtime will fail. - assertThatThrownBy(entityManagerFactory::getMetamodel) + CreationException e = assertThrows(CreationException.class, () -> entityManagerFactory.getMetamodel()); + assertThat(e.getCause()) .isInstanceOf(IllegalStateException.class) .hasMessageContainingAll( "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit default-reactive", @@ -46,7 +49,8 @@ public void sessionFactory() { // So the bean cannot be null. assertThat(sessionFactory).isNotNull(); // However, any attempt to use it at runtime will fail. - assertThatThrownBy(sessionFactory::getMetamodel) + CreationException e = assertThrows(CreationException.class, () -> sessionFactory.getMetamodel()); + assertThat(e.getCause()) .isInstanceOf(IllegalStateException.class) .hasMessageContainingAll( "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit default-reactive",