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 0f46488c531c3..ed0354c6797a0 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 @@ -17,6 +17,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; @@ -26,6 +27,7 @@ import javax.inject.Singleton; import javax.persistence.SharedCacheMode; +import javax.persistence.ValidationMode; import javax.persistence.metamodel.StaticMetamodel; import javax.persistence.spi.PersistenceUnitTransactionType; import javax.transaction.TransactionManager; @@ -136,7 +138,9 @@ CapabilityBuildItem capability() { @BuildStep void checkTransactionsSupport(Capabilities capabilities) { - if (capabilities.isMissing(Capability.TRANSACTIONS)) { + // JTA is necessary for blocking Hibernate ORM but not necessarily for Hibernate Reactive + if (capabilities.isMissing(Capability.TRANSACTIONS) + && capabilities.isMissing(Capability.HIBERNATE_REACTIVE)) { throw new ConfigurationException("The Hibernate ORM extension is only functional in a JTA environment."); } } @@ -236,6 +240,7 @@ public void configurationDescriptorBuilding( LaunchModeBuildItem launchMode, JpaEntitiesBuildItem jpaEntities, List nonJpaModelBuildItems, + Capabilities capabilities, BuildProducer systemProperties, BuildProducer nativeImageResources, BuildProducer hotDeploymentWatchedFiles, @@ -255,7 +260,7 @@ public void configurationDescriptorBuilding( if (impliedPU.shouldGenerateImpliedBlockingPersistenceUnit()) { handleHibernateORMWithNoPersistenceXml(hibernateOrmConfig, persistenceXmlDescriptors, - jdbcDataSources, applicationArchivesBuildItem, launchMode.getLaunchMode(), jpaEntities, + jdbcDataSources, applicationArchivesBuildItem, launchMode.getLaunchMode(), jpaEntities, capabilities, systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors); } } @@ -528,6 +533,7 @@ private void handleHibernateORMWithNoPersistenceXml( ApplicationArchivesBuildItem applicationArchivesBuildItem, LaunchMode launchMode, JpaEntitiesBuildItem jpaEntities, + Capabilities capabilities, BuildProducer systemProperties, BuildProducer nativeImageResources, BuildProducer hotDeploymentWatchedFiles, @@ -550,7 +556,7 @@ private void handleHibernateORMWithNoPersistenceXml( .filter(i -> i.isDefault()) .findFirst(); - Set> storageEngines = new HashSet<>(); + Set storageEngineCollector = new HashSet<>(); if ((defaultJdbcDataSource.isPresent() && hibernateOrmConfig.persistenceUnits.isEmpty()) || hibernateOrmConfig.defaultPersistenceUnit.isAnyPropertySet()) { @@ -559,10 +565,9 @@ private void handleHibernateORMWithNoPersistenceXml( hibernateOrmConfig.defaultPersistenceUnit, modelClassesPerPersistencesUnits.getOrDefault(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, Collections.emptySet()), - jdbcDataSources, applicationArchivesBuildItem, launchMode, - systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors); - - storageEngines.add(hibernateOrmConfig.defaultPersistenceUnit.dialect.storageEngine); + jdbcDataSources, applicationArchivesBuildItem, launchMode, capabilities, + systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors, + storageEngineCollector); } for (Entry persistenceUnitEntry : hibernateOrmConfig.persistenceUnits @@ -570,13 +575,12 @@ private void handleHibernateORMWithNoPersistenceXml( producePersistenceUnitDescriptorFromConfig( hibernateOrmConfig, persistenceUnitEntry.getKey(), persistenceUnitEntry.getValue(), modelClassesPerPersistencesUnits.getOrDefault(persistenceUnitEntry.getKey(), Collections.emptySet()), - jdbcDataSources, applicationArchivesBuildItem, launchMode, - systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors); - - storageEngines.add(persistenceUnitEntry.getValue().dialect.storageEngine); + jdbcDataSources, applicationArchivesBuildItem, launchMode, capabilities, + systemProperties, nativeImageResources, hotDeploymentWatchedFiles, persistenceUnitDescriptors, + storageEngineCollector); } - if (storageEngines.size() > 1) { + if (storageEngineCollector.size() > 1) { throw new ConfigurationException( "The dialect storage engine is a global configuration property: it must be consistent across all persistence units."); } @@ -590,10 +594,12 @@ private static void producePersistenceUnitDescriptorFromConfig( List jdbcDataSources, ApplicationArchivesBuildItem applicationArchivesBuildItem, LaunchMode launchMode, + Capabilities capabilities, BuildProducer systemProperties, BuildProducer nativeImageResources, BuildProducer hotDeploymentWatchedFiles, - BuildProducer persistenceUnitDescriptors) { + BuildProducer persistenceUnitDescriptors, + Set storageEngineCollector) { // Find the associated datasource JdbcDataSourceBuildItem jdbcDataSource; String dataSource; @@ -770,6 +776,17 @@ private static void producePersistenceUnitDescriptorFromConfig( p.put(JPA_SHARED_CACHE_MODE, SharedCacheMode.NONE); } + // Hibernate Validator integration: we force the callback mode to have bootstrap errors reported rather than validation ignored + // if there is any issue when bootstrapping Hibernate Validator. + if (capabilities.isPresent(Capability.HIBERNATE_VALIDATOR)) { + descriptor.getProperties().setProperty(AvailableSettings.JPA_VALIDATION_MODE, ValidationMode.CALLBACK.name()); + } + + // Collect the storage engines if MySQL or MariaDB + if (isMySQLOrMariaDB(dialect.get()) && persistenceUnitConfig.dialect.storageEngine.isPresent()) { + storageEngineCollector.add(persistenceUnitConfig.dialect.storageEngine.get()); + } + persistenceUnitDescriptors.produce( new PersistenceUnitDescriptorBuildItem(descriptor, dataSource, getMultiTenancyStrategy(hibernateOrmConfig), @@ -983,4 +1000,9 @@ private static Class[] toArray(final Set> interfaces) { } return interfaces.toArray(new Class[interfaces.size()]); } + + private static boolean isMySQLOrMariaDB(String dialect) { + String lowercaseDialect = dialect.toLowerCase(Locale.ROOT); + return lowercaseDialect.contains("mysql") || lowercaseDialect.contains("mariadb"); + } } diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsInconsistentStorageEnginesTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsInconsistentStorageEnginesTest.java index 7f9608cc8df04..e9b985539d6c4 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsInconsistentStorageEnginesTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsInconsistentStorageEnginesTest.java @@ -1,5 +1,6 @@ package io.quarkus.hibernate.orm.multiplepersistenceunits; +import org.hibernate.dialect.H2Dialect; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Assertions; @@ -30,4 +31,10 @@ public void testInvalidConfiguration() { // deployment exception should happen first Assertions.fail(); } + + /** + * This is just to have the dialect matching MySQL and trigger the MySQL + storage engines check. + */ + public static class H2DialectWithMySQLInTheName extends H2Dialect { + } } diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsUndefinedPackagesTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsUndefinedPackagesTest.java index 6191fde8592db..fd6d279aaf678 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsUndefinedPackagesTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsUndefinedPackagesTest.java @@ -17,7 +17,8 @@ public class MultiplePersistenceUnitsUndefinedPackagesTest { .setExpectedException(ConfigurationException.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .addClass(User.class) - .addAsResource("application-multiple-persistence-units-invalid.properties", "application.properties")); + .addAsResource("application-multiple-persistence-units-undefined-packages.properties", + "application.properties")); @Test public void testInvalidConfiguration() { diff --git a/extensions/hibernate-orm/deployment/src/test/resources/application-multiple-persistence-units-inconsistent-storage-engines.properties b/extensions/hibernate-orm/deployment/src/test/resources/application-multiple-persistence-units-inconsistent-storage-engines.properties index 439a990e889c3..c96d09e31c934 100644 --- a/extensions/hibernate-orm/deployment/src/test/resources/application-multiple-persistence-units-inconsistent-storage-engines.properties +++ b/extensions/hibernate-orm/deployment/src/test/resources/application-multiple-persistence-units-inconsistent-storage-engines.properties @@ -7,18 +7,18 @@ quarkus.datasource.users.jdbc.url=jdbc:h2:mem:users;DB_CLOSE_DELAY=-1 quarkus.datasource.inventory.db-kind=h2 quarkus.datasource.inventory.jdbc.url=jdbc:h2:mem:inventory;DB_CLOSE_DELAY=-1 -quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect +quarkus.hibernate-orm.dialect=io.quarkus.hibernate.orm.multiplepersistenceunits.MultiplePersistenceUnitsInconsistentStorageEnginesTest$H2DialectWithMySQLInTheName quarkus.hibernate-orm.dialect.storage-engine=engine1 quarkus.hibernate-orm.database.generation=drop-and-create quarkus.hibernate-orm.packages=io.quarkus.hibernate.orm.multiplepersistenceunits.model -quarkus.hibernate-orm."users".dialect=org.hibernate.dialect.H2Dialect +quarkus.hibernate-orm."users".dialect=io.quarkus.hibernate.orm.multiplepersistenceunits.MultiplePersistenceUnitsInconsistentStorageEnginesTest$H2DialectWithMySQLInTheName quarkus.hibernate-orm."users".dialect.storage-engine=engine2 quarkus.hibernate-orm."users".database.generation=drop-and-create quarkus.hibernate-orm."users".datasource=users quarkus.hibernate-orm."users".packages=io.quarkus.hibernate.orm.multiplepersistenceunits.model.user -quarkus.hibernate-orm."inventory".dialect=org.hibernate.dialect.H2Dialect +quarkus.hibernate-orm."inventory".dialect=io.quarkus.hibernate.orm.multiplepersistenceunits.MultiplePersistenceUnitsInconsistentStorageEnginesTest$H2DialectWithMySQLInTheName quarkus.hibernate-orm."inventory".database.generation=drop-and-create quarkus.hibernate-orm."inventory".datasource=inventory quarkus.hibernate-orm."inventory".packages=io.quarkus.hibernate.orm.multiplepersistenceunits.model.inventory diff --git a/extensions/hibernate-orm/deployment/src/test/resources/application-multiple-persistence-units-invalid.properties b/extensions/hibernate-orm/deployment/src/test/resources/application-multiple-persistence-units-undefined-packages.properties similarity index 100% rename from extensions/hibernate-orm/deployment/src/test/resources/application-multiple-persistence-units-invalid.properties rename to extensions/hibernate-orm/deployment/src/test/resources/application-multiple-persistence-units-undefined-packages.properties