Skip to content

Commit

Permalink
Hibernate ORM and Hibernate Reactive cannot be used in the same appli…
Browse files Browse the repository at this point in the history
  • Loading branch information
lucamolteni committed Feb 19, 2025
1 parent 4ec24b1 commit 26f5863
Show file tree
Hide file tree
Showing 19 changed files with 623 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.hibernate.orm.PersistenceUnit;
import io.quarkus.hibernate.orm.ReactivePersistenceUnit;
import io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder;
import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig;
import io.quarkus.hibernate.orm.runtime.JPAConfig;
Expand Down Expand Up @@ -222,11 +223,21 @@ void generateDataSourceBeans(HibernateOrmRecorder recorder,
String persistenceUnitConfigName = persistenceUnitDescriptor.getConfigurationName();
boolean isDefaultPU = PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitConfigName);
boolean isNamedPU = !isDefaultPU;
boolean isReactive = persistenceUnitDescriptor.isReactive();

ExtendedBeanConfigurator persistenceUnitBean = createSyntheticBean(persistenceUnitName,
isDefaultPU,
isNamedPU,
SessionFactory.class,
SESSION_FACTORY_EXPOSED_TYPES,
true);

if (isReactive) {
persistenceUnitBean.addQualifier().annotation(ReactivePersistenceUnit.class).done();
}

syntheticBeanBuildItemBuildProducer
.produce(createSyntheticBean(persistenceUnitName,
isDefaultPU, isNamedPU,
SessionFactory.class, SESSION_FACTORY_EXPOSED_TYPES, true)
.produce(persistenceUnitBean
.createWith(recorder.sessionFactorySupplier(persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(JPAConfig.class)))
.done());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,16 +285,7 @@ public void parsePersistenceXmlDescriptors(HibernateOrmConfig config,
public ImpliedBlockingPersistenceUnitTypeBuildItem defineTypeOfImpliedPU(
List<JdbcDataSourceBuildItem> jdbcDataSourcesBuildItem, //This is from Agroal SPI: safe to use even for Hibernate Reactive
Capabilities capabilities) {
// If we have some blocking datasources defined, we can have an implied PU
if (capabilities.isPresent(Capability.HIBERNATE_REACTIVE)) {
// if we don't have any blocking datasources and Hibernate Reactive is present,
// we don't want a blocking persistence unit
return ImpliedBlockingPersistenceUnitTypeBuildItem.none();
} else {
// even if we don't have any JDBC datasource, we trigger the implied blocking persistence unit
// to properly trigger error conditions and error messages to guide the user
return ImpliedBlockingPersistenceUnitTypeBuildItem.generateImpliedPersistenceUnit();
}
return ImpliedBlockingPersistenceUnitTypeBuildItem.generateImpliedPersistenceUnit();
}

@BuildStep
Expand Down Expand Up @@ -861,7 +852,8 @@ private void handleHibernateORMWithNoPersistenceXml(
|| hibernateOrmConfig.defaultPersistenceUnit().isAnyPropertySet();

Map<String, Set<String>> modelClassesAndPackagesPerPersistencesUnits = getModelClassesAndPackagesPerPersistenceUnits(
hibernateOrmConfig, jpaModel, index.getIndex(), enableDefaultPersistenceUnit);
hibernateOrmConfig, jpaModel, index.getIndex(), enableDefaultPersistenceUnit,
PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME);
Set<String> modelClassesAndPackagesForDefaultPersistenceUnit = modelClassesAndPackagesPerPersistencesUnits
.getOrDefault(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, Collections.emptySet());

Expand Down Expand Up @@ -942,7 +934,8 @@ 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 Properties());
new Properties(),
false);

MultiTenancyStrategy multiTenancyStrategy = getMultiTenancyStrategy(persistenceUnitConfig.multitenant());
collectDialectConfig(persistenceUnitName, persistenceUnitConfig,
Expand Down Expand Up @@ -1302,7 +1295,8 @@ private void enhanceEntities(final JpaModelBuildItem jpaModel,
}

public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceUnits(HibernateOrmConfig hibernateOrmConfig,
JpaModelBuildItem jpaModel, IndexView index, boolean enableDefaultPersistenceUnit) {
JpaModelBuildItem jpaModel, IndexView index, boolean enableDefaultPersistenceUnit,
String defaultPersistenceUnitName) {
Map<String, Set<String>> modelClassesAndPackagesPerPersistenceUnits = new HashMap<>();

boolean hasPackagesInQuarkusConfig = hasPackagesInQuarkusConfig(hibernateOrmConfig);
Expand All @@ -1327,7 +1321,7 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU

for (String packageName : hibernateOrmConfig.defaultPersistenceUnit().packages().get()) {
packageRules.computeIfAbsent(normalizePackage(packageName), p -> new HashSet<>())
.add(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME);
.add(defaultPersistenceUnitName);
}
}

Expand Down Expand Up @@ -1371,7 +1365,7 @@ public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceU
// so we don't need to split them
Set<String> allModelClassesAndPackages = new HashSet<>(jpaModel.getAllModelClassNames());
allModelClassesAndPackages.addAll(jpaModel.getAllModelPackageNames());
return Collections.singletonMap(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, allModelClassesAndPackages);
return Collections.singletonMap(defaultPersistenceUnitName, allModelClassesAndPackages);
}

Set<String> modelClassesWithPersistenceUnitAnnotations = new TreeSet<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,18 @@ public boolean isFromPersistenceXml() {
return fromPersistenceXml;
}

public boolean isReactive() {
return isReactive;
}

public boolean isHibernateValidatorPresent() {
return isHibernateValidatorPresent;
}

public QuarkusPersistenceUnitDefinition asOutputPersistenceUnitDefinition(
List<HibernateOrmIntegrationStaticDescriptor> integrationStaticDescriptors) {
return new QuarkusPersistenceUnitDefinition(descriptor, config,
xmlMappings, isReactive, fromPersistenceXml, isHibernateValidatorPresent,
jsonMapper, xmlMapper, integrationStaticDescriptors);
xmlMappings, isReactive, fromPersistenceXml, isHibernateValidatorPresent, jsonMapper, xmlMapper, integrationStaticDescriptors);
}

private Optional<FormatMapperKind> json(Capabilities capabilities) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.hibernate.orm;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import jakarta.inject.Qualifier;
import jakarta.persistence.EntityManagerFactory;

/**
* This is used to qualify the {@link EntityManagerFactory} that should be used for Hibernate Reactive.
*/
@Target({ TYPE, FIELD, METHOD, PARAMETER, PACKAGE })
@Retention(RUNTIME)
@Documented
@Qualifier
public @interface ReactivePersistenceUnit {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.quarkus.hibernate.orm.runtime;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import jakarta.persistence.spi.PersistenceProvider;
import jakarta.persistence.spi.PersistenceProviderResolver;

public final class MultiplePersistenceProviderResolver implements PersistenceProviderResolver {

private final List<PersistenceProvider> persistenceProviders = new ArrayList<>();

public MultiplePersistenceProviderResolver(PersistenceProvider... persistenceProviders) {
this.persistenceProviders.addAll(List.of(persistenceProviders));
}

@Override
public List<PersistenceProvider> getPersistenceProviders() {
return Collections.unmodifiableList(persistenceProviders);
}

public void addPersistenceProvider(PersistenceProvider persistenceProvider) {
persistenceProviders.add(persistenceProvider);
}

@Override
public void clearCachedProviders() {
// done!
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import java.util.List;
import java.util.Map;

import jakarta.persistence.spi.PersistenceProviderResolver;
import jakarta.persistence.spi.PersistenceProviderResolverHolder;

import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeDescriptor;

public final class PersistenceProviderSetup {
Expand All @@ -18,8 +21,19 @@ public static void registerStaticInitPersistenceProvider() {

public static void registerRuntimePersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig,
Map<String, List<HibernateOrmIntegrationRuntimeDescriptor>> integrationRuntimeDescriptors) {
jakarta.persistence.spi.PersistenceProviderResolverHolder.setPersistenceProviderResolver(
new SingletonPersistenceProviderResolver(
new FastBootHibernatePersistenceProvider(hibernateOrmRuntimeConfig, integrationRuntimeDescriptors)));

PersistenceProviderResolver persistenceProviderResolver = PersistenceProviderResolverHolder
.getPersistenceProviderResolver();
if (persistenceProviderResolver == null ||
(persistenceProviderResolver != null
&& !(persistenceProviderResolver instanceof MultiplePersistenceProviderResolver))) {
persistenceProviderResolver = new MultiplePersistenceProviderResolver();
PersistenceProviderResolverHolder.setPersistenceProviderResolver(persistenceProviderResolver);
}

((MultiplePersistenceProviderResolver) persistenceProviderResolver)
.addPersistenceProvider(new FastBootHibernatePersistenceProvider(hibernateOrmRuntimeConfig,
integrationRuntimeDescriptors));

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ public final class QuarkusPersistenceUnitDescriptor implements PersistenceUnitDe
private final SharedCacheMode sharedCacheMode;
private final List<String> managedClassNames;
private final Properties properties;
private final boolean reactive;

public QuarkusPersistenceUnitDescriptor(String name, String configurationName,
PersistenceUnitTransactionType transactionType,
List<String> managedClassNames,
Properties properties) {
Properties properties, boolean reactive) {
this.name = name;
this.configurationName = configurationName;
this.providerClassName = null;
Expand All @@ -44,6 +45,7 @@ public QuarkusPersistenceUnitDescriptor(String name, String configurationName,
this.sharedCacheMode = null;
this.managedClassNames = managedClassNames;
this.properties = properties;
this.reactive = reactive;
}

/**
Expand All @@ -57,7 +59,7 @@ public QuarkusPersistenceUnitDescriptor(String name, String configurationName,
String providerClassName, boolean useQuotedIdentifiers,
PersistenceUnitTransactionType transactionType,
ValidationMode validationMode, SharedCacheMode sharedCacheMode, List<String> managedClassNames,
Properties properties) {
Properties properties, boolean reactive) {
this.name = name;
this.configurationName = configurationName;
this.providerClassName = providerClassName;
Expand All @@ -67,6 +69,7 @@ public QuarkusPersistenceUnitDescriptor(String name, String configurationName,
this.sharedCacheMode = sharedCacheMode;
this.managedClassNames = managedClassNames;
this.properties = properties;
this.reactive = reactive;
}

/**
Expand All @@ -87,7 +90,7 @@ public static QuarkusPersistenceUnitDescriptor validateAndReadFrom(PersistenceUn
return new QuarkusPersistenceUnitDescriptor(toClone.getName(), toClone.getName(), toClone.getProviderClassName(),
toClone.isUseQuotedIdentifiers(),
toClone.getTransactionType(), toClone.getValidationMode(), toClone.getSharedCacheMode(),
Collections.unmodifiableList(toClone.getManagedClassNames()), toClone.getProperties());
Collections.unmodifiableList(toClone.getManagedClassNames()), toClone.getProperties(), false);
}

@Override
Expand Down Expand Up @@ -179,6 +182,10 @@ public ClassLoader getTempClassLoader() {
return null;
}

public boolean isReactive() {
return reactive;
}

@Override
public void pushClassTransformer(final EnhancementContext enhancementContext) {
// has never been supported
Expand Down Expand Up @@ -208,10 +215,18 @@ private static void verifyIgnoredFields(final PersistenceUnitDescriptor toClone)

@Override
public String toString() {
return "PersistenceUnitDescriptor{" + "name='" + name + '\'' + ", providerClassName='" + providerClassName
+ '\'' + ", useQuotedIdentifiers=" + useQuotedIdentifiers + ", transactionType=" + transactionType
+ ", validationMode=" + validationMode + ", sharedCachemode=" + sharedCacheMode + ", managedClassNames="
+ managedClassNames + ", properties=" + properties + '}';
return "QuarkusPersistenceUnitDescriptor{" +
"name='" + name + '\'' +
", configurationName='" + configurationName + '\'' +
", providerClassName='" + providerClassName + '\'' +
", useQuotedIdentifiers=" + useQuotedIdentifiers +
", transactionType=" + transactionType +
", validationMode=" + validationMode +
", sharedCacheMode=" + sharedCacheMode +
", managedClassNames=" + managedClassNames +
", properties=" + properties +
", isReactive=" + reactive +
'}';
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;
import static io.quarkus.hibernate.orm.deployment.HibernateConfigUtil.firstPresent;
import static io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME;
import static io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.REACTIVE_PERSISTENCE_UNIT_NAME;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DB_VERSION;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_SHARED_CACHE_MODE;
import static org.hibernate.cfg.AvailableSettings.STORAGE_ENGINE;
Expand Down Expand Up @@ -100,15 +100,7 @@ void registerBeans(BuildProducer<AdditionalBeanBuildItem> additionalBeans, Combi
ReactiveSessionFactoryProducer.class.getSimpleName());
return;
}
if (descriptors.size() == 1) {
// Only register the bean if their EMF dependency is also available, so use the same guard as the ORM extension
additionalBeans.produce(new AdditionalBeanBuildItem(ReactiveSessionFactoryProducer.class));
} else {
throw new ConfigurationException(
"The Hibernate Reactive extension requires exactly one persistence unit configured: " + descriptors
.stream()
.map(PersistenceUnitDescriptorBuildItem::getPersistenceUnitName).collect(Collectors.toList()));
}
additionalBeans.produce(new AdditionalBeanBuildItem(ReactiveSessionFactoryProducer.class));
}

@BuildStep
Expand Down Expand Up @@ -262,12 +254,13 @@ private static QuarkusPersistenceUnitDescriptor generateReactivePersistenceUnit(
BuildProducer<HotDeploymentWatchedFileBuildItem> hotDeploymentWatchedFiles,
List<DatabaseKindDialectBuildItem> dbKindDialectBuildItems) {
//we have no persistence.xml so we will create a default one
String persistenceUnitConfigName = DEFAULT_PERSISTENCE_UNIT_NAME;
String persistenceUnitConfigName = REACTIVE_PERSISTENCE_UNIT_NAME;

Map<String, Set<String>> modelClassesAndPackagesPerPersistencesUnits = HibernateOrmProcessor
.getModelClassesAndPackagesPerPersistenceUnits(hibernateOrmConfig, jpaModel, index.getIndex(), true);
.getModelClassesAndPackagesPerPersistenceUnits(hibernateOrmConfig, jpaModel, index.getIndex(), true,
REACTIVE_PERSISTENCE_UNIT_NAME);
Set<String> nonDefaultPUWithModelClassesOrPackages = modelClassesAndPackagesPerPersistencesUnits.entrySet().stream()
.filter(e -> !DEFAULT_PERSISTENCE_UNIT_NAME.equals(e.getKey()) && !e.getValue().isEmpty())
.filter(e -> !REACTIVE_PERSISTENCE_UNIT_NAME.equals(e.getKey()) && !e.getValue().isEmpty())
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
if (!nonDefaultPUWithModelClassesOrPackages.isEmpty()) {
Expand All @@ -277,7 +270,7 @@ private static QuarkusPersistenceUnitDescriptor generateReactivePersistenceUnit(
nonDefaultPUWithModelClassesOrPackages);
}
Set<String> modelClassesAndPackages = modelClassesAndPackagesPerPersistencesUnits
.getOrDefault(DEFAULT_PERSISTENCE_UNIT_NAME, Collections.emptySet());
.getOrDefault(REACTIVE_PERSISTENCE_UNIT_NAME, Collections.emptySet());

if (modelClassesAndPackages.isEmpty()) {
LOG.warnf("Could not find any entities affected to the Hibernate Reactive persistence unit.");
Expand All @@ -287,7 +280,8 @@ private static QuarkusPersistenceUnitDescriptor generateReactivePersistenceUnit(
HibernateReactive.DEFAULT_REACTIVE_PERSISTENCE_UNIT_NAME, persistenceUnitConfigName,
PersistenceUnitTransactionType.RESOURCE_LOCAL,
new ArrayList<>(modelClassesAndPackages),
new Properties());
new Properties(),
true);

setDialectAndStorageEngine(dbKindOptional, explicitDialect, explicitDbMinVersion, dbKindDialectBuildItems,
persistenceUnitConfig.dialect().storageEngine(), systemProperties, desc);
Expand Down Expand Up @@ -432,7 +426,7 @@ private static void setDialectAndStorageEngine(Optional<String> dbKind, Optional
Optional<String> explicitDbMinVersion, List<DatabaseKindDialectBuildItem> dbKindDialectBuildItems,
Optional<String> storageEngine, BuildProducer<SystemPropertyBuildItem> systemProperties,
QuarkusPersistenceUnitDescriptor desc) {
final String persistenceUnitName = DEFAULT_PERSISTENCE_UNIT_NAME;
final String persistenceUnitName = REACTIVE_PERSISTENCE_UNIT_NAME;
Optional<String> dialect = explicitDialect;
Optional<String> dbProductName = Optional.empty();
Optional<String> dbProductVersion = explicitDbMinVersion;
Expand Down
Loading

0 comments on commit 26f5863

Please sign in to comment.