diff --git a/build-parent/pom.xml b/build-parent/pom.xml
index 1727fc8145d31..4f8dd100ea901 100644
--- a/build-parent/pom.xml
+++ b/build-parent/pom.xml
@@ -170,6 +170,7 @@
${enforcer.skip}
+ ${surefire.argLine.additional}
1.7.0
@@ -460,7 +461,7 @@
${project.groupId}
- -Djava.io.tmpdir="${project.build.directory}"
+ -Djava.io.tmpdir="${project.build.directory}" ${failsafe.argLine.additional}
MAVEN_OPTS
diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java
index 8f750396872c5..049ce282705f8 100644
--- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java
+++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java
@@ -5,11 +5,13 @@
import java.util.TreeMap;
import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil;
+import io.quarkus.hibernate.orm.runtime.config.DatabaseOrmCompatibilityVersion;
import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigDocSection;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigRoot;
+import io.quarkus.runtime.annotations.ConvertWith;
@ConfigRoot
public class HibernateOrmConfig {
@@ -26,6 +28,33 @@ public class HibernateOrmConfig {
@ConfigItem(defaultValue = "true")
public boolean enabled;
+ /**
+ * When set, attempts to exchange data with the database
+ * as the given version of Hibernate ORM would have,
+ * *on a best-effort basis*.
+ *
+ * Please note:
+ *
+ * * schema validation may still fail in some cases:
+ * this attempts to make Hibernate ORM 6+ behave correctly at runtime,
+ * but it may still expect a different (but runtime-compatible) schema.
+ * * robust test suites are still useful and recommended:
+ * you should still check that your application behaves as intended with your legacy schema.
+ * * this feature is inherently unstable:
+ * some aspects of it may stop working in future versions of Quarkus,
+ * and older versions will be dropped as Hibernate ORM changes pile up
+ * and support for those older versions becomes too unreliable.
+ * * you should still plan a migration of your schema to a newer version of Hibernate ORM.
+ * For help with migration, refer to
+ * link:https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.0:-Hibernate-ORM-5-to-6-migration[the Quarkus 3
+ * migration guide from Hibernate ORM 5 to 6].
+ *
+ * @asciidoclet
+ */
+ @ConfigItem(name = "database.orm-compatibility.version", defaultValue = "LATEST")
+ @ConvertWith(DatabaseOrmCompatibilityVersion.Converter.class)
+ public DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion;
+
/**
* Configuration for the default persistence unit.
*/
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 9fa96ab743751..e0792c9571d8e 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
@@ -382,17 +382,21 @@ public void configurationDescriptorBuilding(
// First produce the PUs having a persistence.xml: these are not reactive, as we don't allow using a persistence.xml for them.
for (PersistenceXmlDescriptorBuildItem persistenceXmlDescriptorBuildItem : persistenceXmlDescriptors) {
ParsedPersistenceXmlDescriptor xmlDescriptor = persistenceXmlDescriptorBuildItem.getDescriptor();
+ Optional jdbcDataSource = jdbcDataSources.stream()
+ .filter(i -> i.isDefault())
+ .findFirst();
persistenceUnitDescriptors
.produce(new PersistenceUnitDescriptorBuildItem(xmlDescriptor,
xmlDescriptor.getName(),
Optional.of(DataSourceUtil.DEFAULT_DATASOURCE_NAME),
+ jdbcDataSource.map(JdbcDataSourceBuildItem::getDbKind),
getMultiTenancyStrategy(Optional.ofNullable(persistenceXmlDescriptorBuildItem.getDescriptor()
.getProperties().getProperty("hibernate.multiTenancy"))), //FIXME this property is meaningless in Hibernate ORM 6
null,
jpaModel.getXmlMappings(persistenceXmlDescriptorBuildItem.getDescriptor().getName()),
Collections.emptyMap(),
- false,
- true));
+ hibernateOrmConfig.databaseOrmCompatibilityVersion,
+ false, true));
}
if (impliedPU.shouldGenerateImpliedBlockingPersistenceUnit()) {
@@ -1196,10 +1200,12 @@ private static void producePersistenceUnitDescriptorFromConfig(
persistenceUnitDescriptors.produce(
new PersistenceUnitDescriptorBuildItem(descriptor, descriptor.getName(),
jdbcDataSource.map(JdbcDataSourceBuildItem::getName),
+ jdbcDataSource.map(JdbcDataSourceBuildItem::getDbKind),
multiTenancyStrategy,
persistenceUnitConfig.multitenantSchemaDatasource.orElse(null),
xmlMappings,
persistenceUnitConfig.unsupportedProperties,
+ hibernateOrmConfig.databaseOrmCompatibilityVersion,
false, false));
}
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 f9b5b0a7253cd..df35ac0ab26fc 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
@@ -11,6 +11,7 @@
import io.quarkus.datasource.common.runtime.DataSourceUtil;
import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDefinition;
import io.quarkus.hibernate.orm.runtime.boot.xml.RecordableXmlMapping;
+import io.quarkus.hibernate.orm.runtime.config.DatabaseOrmCompatibilityVersion;
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticDescriptor;
import io.quarkus.hibernate.orm.runtime.migration.MultiTenancyStrategy;
@@ -29,35 +30,43 @@ public final class PersistenceUnitDescriptorBuildItem extends MultiBuildItem {
// use the name "", so we need to convert between those.
private final String configurationName;
private final Optional dataSource;
+ private final Optional dbKind;
private final MultiTenancyStrategy multiTenancyStrategy;
private final String multiTenancySchemaDataSource;
private final List xmlMappings;
private final Map quarkusConfigUnsupportedProperties;
+ private final DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion;
private final boolean isReactive;
private final boolean fromPersistenceXml;
public PersistenceUnitDescriptorBuildItem(ParsedPersistenceXmlDescriptor descriptor, String configurationName,
+ Optional dbKind,
List xmlMappings,
Map quarkusConfigUnsupportedProperties,
+ DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion,
boolean isReactive, boolean fromPersistenceXml) {
this(descriptor, configurationName,
- Optional.of(DataSourceUtil.DEFAULT_DATASOURCE_NAME), MultiTenancyStrategy.NONE, null,
- xmlMappings, quarkusConfigUnsupportedProperties, isReactive, fromPersistenceXml);
+ Optional.of(DataSourceUtil.DEFAULT_DATASOURCE_NAME), dbKind, MultiTenancyStrategy.NONE, null,
+ xmlMappings, quarkusConfigUnsupportedProperties, databaseOrmCompatibilityVersion,
+ isReactive, fromPersistenceXml);
}
public PersistenceUnitDescriptorBuildItem(ParsedPersistenceXmlDescriptor descriptor, String configurationName,
- Optional dataSource,
+ Optional dataSource, Optional dbKind,
MultiTenancyStrategy multiTenancyStrategy, String multiTenancySchemaDataSource,
List xmlMappings,
Map quarkusConfigUnsupportedProperties,
+ DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion,
boolean isReactive, boolean fromPersistenceXml) {
this.descriptor = descriptor;
this.configurationName = configurationName;
this.dataSource = dataSource;
+ this.dbKind = dbKind;
this.multiTenancyStrategy = multiTenancyStrategy;
this.multiTenancySchemaDataSource = multiTenancySchemaDataSource;
this.xmlMappings = xmlMappings;
this.quarkusConfigUnsupportedProperties = quarkusConfigUnsupportedProperties;
+ this.databaseOrmCompatibilityVersion = databaseOrmCompatibilityVersion;
this.isReactive = isReactive;
this.fromPersistenceXml = fromPersistenceXml;
}
@@ -100,8 +109,9 @@ public boolean isFromPersistenceXml() {
public QuarkusPersistenceUnitDefinition asOutputPersistenceUnitDefinition(
List integrationStaticDescriptors) {
- return new QuarkusPersistenceUnitDefinition(descriptor, configurationName, dataSource, multiTenancyStrategy,
- xmlMappings,
- quarkusConfigUnsupportedProperties, isReactive, fromPersistenceXml, integrationStaticDescriptors);
+ return new QuarkusPersistenceUnitDefinition(descriptor, configurationName, dataSource, dbKind,
+ multiTenancyStrategy, xmlMappings,
+ quarkusConfigUnsupportedProperties, databaseOrmCompatibilityVersion,
+ isReactive, fromPersistenceXml, integrationStaticDescriptors);
}
}
diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/unsupportedproperties/SettingsSpyingIdentifierGenerator.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/SettingsSpyingIdentifierGenerator.java
similarity index 89%
rename from extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/unsupportedproperties/SettingsSpyingIdentifierGenerator.java
rename to extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/SettingsSpyingIdentifierGenerator.java
index b92afcae634ff..4fbd40b8261d1 100644
--- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/unsupportedproperties/SettingsSpyingIdentifierGenerator.java
+++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/SettingsSpyingIdentifierGenerator.java
@@ -1,4 +1,4 @@
-package io.quarkus.hibernate.orm.config.unsupportedproperties;
+package io.quarkus.hibernate.orm.config;
import java.io.Serializable;
import java.util.ArrayList;
@@ -20,7 +20,7 @@
* Feel free to use some other solution if you find one.
*/
public class SettingsSpyingIdentifierGenerator implements IdentifierGenerator {
- static final List> collectedSettings = new ArrayList<>();
+ public static final List> collectedSettings = new ArrayList<>();
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/databaseormcompatibility/DatabaseOrmCompatibilityVersionTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/databaseormcompatibility/DatabaseOrmCompatibilityVersionTest.java
new file mode 100644
index 0000000000000..021f341bdd22a
--- /dev/null
+++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/databaseormcompatibility/DatabaseOrmCompatibilityVersionTest.java
@@ -0,0 +1,113 @@
+package io.quarkus.hibernate.orm.config.databaseormcompatibility;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Map;
+import java.util.logging.Formatter;
+import java.util.logging.Level;
+
+import jakarta.inject.Inject;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.hibernate.cfg.AvailableSettings;
+import org.jboss.logmanager.formatters.PatternFormatter;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.hibernate.orm.config.SettingsSpyingIdentifierGenerator;
+import io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider;
+import io.quarkus.test.QuarkusUnitTest;
+
+public class DatabaseOrmCompatibilityVersionTest {
+
+ private static final Formatter LOG_FORMATTER = new PatternFormatter("%s");
+
+ @RegisterExtension
+ static QuarkusUnitTest runner = new QuarkusUnitTest()
+ .withApplicationRoot((jar) -> jar
+ .addClass(SpyingIdentifierGeneratorEntity.class)
+ .addClass(SettingsSpyingIdentifierGenerator.class))
+ .withConfigurationResource("application.properties")
+ .overrideConfigKey("quarkus.hibernate-orm.database.orm-compatibility.version", "5.6")
+ // We allow overriding database/orm compatibility settings with .unsupported-properties,
+ // to enable step-by-step migration
+ .overrideConfigKey(
+ "quarkus.hibernate-orm.unsupported-properties.\"" + AvailableSettings.PREFERRED_INSTANT_JDBC_TYPE + "\"",
+ "TIMESTAMP_UTC")
+ // Expect warnings on startup
+ .setLogRecordPredicate(record -> FastBootHibernatePersistenceProvider.class.getName().equals(record.getLoggerName())
+ && record.getLevel().intValue() >= Level.WARNING.intValue())
+ .assertLogRecords(records -> {
+ var assertion = assertThat(records)
+ .as("Warnings on startup")
+ .hasSizeGreaterThanOrEqualTo(3);
+ assertion.element(0).satisfies(record -> assertThat(LOG_FORMATTER.formatMessage(record))
+ .contains("Persistence-unit [] sets unsupported properties")
+ // We should not log property values, that could be a security breach for some properties.
+ .doesNotContain("some-value"));
+ assertion.element(1).satisfies(record -> assertThat(LOG_FORMATTER.formatMessage(record))
+ .contains("Persistence-unit []:"
+ + " enabling best-effort backwards compatibility with 'quarkus.hibernate-orm.database.orm-compatibility.version=5.6'.",
+ "Quarkus will attempt to change the behavior and expected schema of Hibernate ORM"
+ + " to match those of Hibernate ORM 5.6.",
+ "This is an inherently best-effort feature",
+ "may stop working in future versions of Quarkus",
+ "Consider migrating your application",
+ "https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.0:-Hibernate-ORM-5-to-6-migration"));
+ assertion.anySatisfy(record -> assertThat(LOG_FORMATTER.formatMessage(record))
+ .contains(
+ "Persistence-unit [] - 5.6 compatibility: setting 'hibernate.timezone.default_storage=NORMALIZE'.",
+ "affects Hibernate ORM's behavior and schema compatibility",
+ "may stop working in future versions of Quarkus"));
+ });
+
+ @Inject
+ EntityManagerFactory emf;
+
+ @Inject
+ EntityManager em;
+
+ @Test
+ public void testPropertiesPropagatedToStaticInit() {
+ assertThat(SettingsSpyingIdentifierGenerator.collectedSettings).hasSize(1);
+ Map settings = SettingsSpyingIdentifierGenerator.collectedSettings.get(0);
+ assertThat(settings).containsAllEntriesOf(Map.of(
+ AvailableSettings.TIMEZONE_DEFAULT_STORAGE, "NORMALIZE",
+ // We allow overriding database/orm compatibility settings with .unsupported-properties,
+ // to enable step-by-step migration
+ AvailableSettings.PREFERRED_INSTANT_JDBC_TYPE, "TIMESTAMP_UTC"));
+ }
+
+ @Test
+ public void testPropertiesPropagatedToRuntimeInit() {
+ assertThat(emf.getProperties()).containsAllEntriesOf(Map.of(
+ AvailableSettings.TIMEZONE_DEFAULT_STORAGE, "NORMALIZE",
+ // We allow overriding database/orm compatibility settings with .unsupported-properties,
+ // to enable step-by-step migration
+ AvailableSettings.PREFERRED_INSTANT_JDBC_TYPE, "TIMESTAMP_UTC"));
+ }
+
+ @Entity
+ public static class SpyingIdentifierGeneratorEntity {
+ @Id
+ @GeneratedValue(generator = "spying-generator")
+ @GenericGenerator(name = "spying-generator", strategy = "io.quarkus.hibernate.orm.config.SettingsSpyingIdentifierGenerator")
+ private Long id;
+
+ public SpyingIdentifierGeneratorEntity() {
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+ }
+}
diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/unsupportedproperties/UnsupportedPropertiesTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/unsupportedproperties/UnsupportedPropertiesTest.java
index 6f1b725efd00d..d5539e8e9a192 100644
--- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/unsupportedproperties/UnsupportedPropertiesTest.java
+++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/unsupportedproperties/UnsupportedPropertiesTest.java
@@ -27,6 +27,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+import io.quarkus.hibernate.orm.config.SettingsSpyingIdentifierGenerator;
import io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider;
import io.quarkus.narayana.jta.QuarkusTransaction;
import io.quarkus.test.QuarkusUnitTest;
@@ -211,7 +212,7 @@ public void setParent(ParentEntity parent) {
public static class SpyingIdentifierGeneratorEntity {
@Id
@GeneratedValue(generator = "spying-generator")
- @GenericGenerator(name = "spying-generator", strategy = "io.quarkus.hibernate.orm.config.unsupportedproperties.SettingsSpyingIdentifierGenerator")
+ @GenericGenerator(name = "spying-generator", strategy = "io.quarkus.hibernate.orm.config.SettingsSpyingIdentifierGenerator")
private Long id;
public SpyingIdentifierGeneratorEntity() {
diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/BuildTimeSettings.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/BuildTimeSettings.java
index 1683c59033538..6ae78cd28f235 100644
--- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/BuildTimeSettings.java
+++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/BuildTimeSettings.java
@@ -1,36 +1,57 @@
package io.quarkus.hibernate.orm.runtime;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Map;
-public class BuildTimeSettings {
+import io.quarkus.hibernate.orm.runtime.config.DatabaseOrmCompatibilityVersion;
- private Map settings;
+public class BuildTimeSettings {
- public BuildTimeSettings(Map settings) {
- this.settings = Collections.unmodifiableMap(new HashMap<>(settings));
+ private Map quarkusConfigSettings;
+ private DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion;
+ private Map databaseOrmCompatibilitySettings;
+ private Map allSettings;
+
+ public BuildTimeSettings(Map quarkusConfigSettings,
+ DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion,
+ Map databaseOrmCompatibilitySettings,
+ Map allSettings) {
+ this.quarkusConfigSettings = Map.copyOf(quarkusConfigSettings);
+ this.databaseOrmCompatibilityVersion = databaseOrmCompatibilityVersion;
+ this.databaseOrmCompatibilitySettings = Map.copyOf(databaseOrmCompatibilitySettings);
+ this.allSettings = Map.copyOf(allSettings);
}
public Object get(String key) {
- return settings.get(key);
+ return allSettings.get(key);
}
public boolean getBoolean(String key) {
- Object propertyValue = settings.get(key);
+ Object propertyValue = allSettings.get(key);
return propertyValue != null && Boolean.parseBoolean(propertyValue.toString());
}
public boolean isConfigured(String key) {
- return settings.containsKey(key);
+ return allSettings.containsKey(key);
+ }
+
+ public Map getQuarkusConfigSettings() {
+ return quarkusConfigSettings;
+ }
+
+ public DatabaseOrmCompatibilityVersion getDatabaseOrmCompatibilityVersion() {
+ return databaseOrmCompatibilityVersion;
+ }
+
+ public Map getDatabaseOrmCompatibilitySettings() {
+ return databaseOrmCompatibilitySettings;
}
- public Map getSettings() {
- return settings;
+ public Map getAllSettings() {
+ return allSettings;
}
@Override
public String toString() {
- return this.getClass().getSimpleName() + " {" + settings.toString() + "}";
+ return this.getClass().getSimpleName() + " {" + allSettings.toString() + "}";
}
}
diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java
index 511bb5b4086f6..b1b02d62ac8df 100644
--- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java
+++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java
@@ -33,6 +33,7 @@
import io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder;
import io.quarkus.hibernate.orm.runtime.boot.RuntimePersistenceUnitDescriptor;
import io.quarkus.hibernate.orm.runtime.boot.registry.PreconfiguredServiceRegistryBuilder;
+import io.quarkus.hibernate.orm.runtime.config.DatabaseOrmCompatibilityVersion;
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeDescriptor;
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener;
import io.quarkus.hibernate.orm.runtime.recording.PrevalidatedQuarkusMetadata;
@@ -253,8 +254,37 @@ private RuntimeSettings buildRuntimeSettings(String persistenceUnitName, Recorde
runtimeSettingsBuilder.put(entry.getKey(), entry.getValue());
}
- RuntimeSettings runtimeSettings = runtimeSettingsBuilder.build();
- return runtimeSettings;
+ var databaseOrmCompatibilityVersion = buildTimeSettings.getDatabaseOrmCompatibilityVersion();
+ var databaseOrmCompatibilitySettings = buildTimeSettings.getDatabaseOrmCompatibilitySettings();
+ if (databaseOrmCompatibilityVersion != DatabaseOrmCompatibilityVersion.LATEST) {
+ log.warnf("Persistence-unit [%1$s]: enabling best-effort backwards compatibility with '%2$s=%3$s'."
+ + " Quarkus will attempt to change the behavior and expected schema of Hibernate ORM"
+ + " to match those of Hibernate ORM %3$s."
+ + " This is an inherently best-effort feature that cannot address all "
+ + " backwards-incompatible changes of Hibernate ORM 6."
+ + " It is also inherently unstable and may stop working in future versions of Quarkus."
+ + " Consider migrating your application to native Hibernate ORM 6 behavior;"
+ + " see https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.0:-Hibernate-ORM-5-to-6-migration for more information.",
+ persistenceUnitName,
+ HibernateOrmRuntimeConfig.puPropertyKey(persistenceUnitName, "database.orm-compatibility.version"),
+ databaseOrmCompatibilityVersion.externalRepresentation,
+ persistenceUnitConfig.unsupportedProperties.keySet());
+ }
+ for (Map.Entry entry : databaseOrmCompatibilitySettings.entrySet()) {
+ var key = entry.getKey();
+ var value = entry.getValue();
+ if (!runtimeSettingsBuilder.isConfigured(key)) {
+ log.warnf("Persistence-unit [%1$s] - %2$s compatibility: setting '%3$s=%4$s'."
+ + " This affects Hibernate ORM's behavior and schema compatibility"
+ + " and may stop working in future versions of Quarkus.",
+ persistenceUnitName,
+ databaseOrmCompatibilityVersion.externalRepresentation,
+ key, value);
+ runtimeSettingsBuilder.put(key, value);
+ }
+ }
+
+ return runtimeSettingsBuilder.build();
}
private StandardServiceRegistry rewireMetadataAndExtractServiceRegistry(RuntimeSettings runtimeSettings, RecordedState rs,
diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/RuntimeSettings.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/RuntimeSettings.java
index 34937dfaad008..28cbdb267a353 100644
--- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/RuntimeSettings.java
+++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/RuntimeSettings.java
@@ -39,7 +39,7 @@ public static class Builder {
private final Map settings;
public Builder(BuildTimeSettings buildTimeSettings, IntegrationSettings integrationSettings) {
- this.settings = new HashMap<>(buildTimeSettings.getSettings());
+ this.settings = new HashMap<>(buildTimeSettings.getQuarkusConfigSettings());
this.settings.putAll(integrationSettings.getSettings());
}
diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java
index c45338a255bcf..9a88c0020f5ac 100644
--- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java
+++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java
@@ -19,6 +19,7 @@
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@@ -137,24 +138,11 @@ public FastBootMetadataBuilder(final QuarkusPersistenceUnitDefinition puDefiniti
final RecordableBootstrap ssrBuilder = RecordableBootstrapFactory.createRecordableBootstrapBuilder(puDefinition);
final MergedSettings mergedSettings = mergeSettings(puDefinition);
- this.buildTimeSettings = new BuildTimeSettings(mergedSettings.getConfigurationValues());
+ this.buildTimeSettings = createBuildTimeSettings(puDefinition, mergedSettings.getConfigurationValues());
// Build the "standard" service registry
- ssrBuilder.applySettings(buildTimeSettings.getSettings());
- // We don't add unsupported properties to mergedSettings/buildTimeSettings,
- // so that we can more easily differentiate between
- // properties coming from Quarkus and "unsupported" properties
- // on startup (see io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider.buildRuntimeSettings)
- for (Map.Entry entry : puDefinition.getQuarkusConfigUnsupportedProperties().entrySet()) {
- var key = entry.getKey();
- if (buildTimeSettings.get(key) != null) {
- // Ignore properties that were already set by Quarkus;
- // we'll log a warning about those on startup.
- // (see io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider.buildRuntimeSettings)
- continue;
- }
- ssrBuilder.applySetting(key, entry.getValue());
- }
+ ssrBuilder.applySettings(buildTimeSettings.getAllSettings());
+
// We need to initialize the multi tenancy strategy before building the service registry as it is used to
// create metadata builder. Adding services afterwards would lead to unpredicted behavior.
final MultiTenancyStrategy multiTenancyStrategy = puDefinition.getMultitenancyStrategy();
@@ -220,6 +208,34 @@ public FastBootMetadataBuilder(final QuarkusPersistenceUnitDefinition puDefiniti
metamodelBuilder.applyTempClassLoader(null);
}
+ private BuildTimeSettings createBuildTimeSettings(QuarkusPersistenceUnitDefinition puDefinition,
+ Map quarkusConfigSettings) {
+ Map quarkusConfigUnsupportedProperties = puDefinition.getQuarkusConfigUnsupportedProperties();
+ Map allSettings = new HashMap<>(quarkusConfigSettings);
+
+ // Ignore properties that were already set by Quarkus;
+ // we'll log a warning about those on startup.
+ // (see io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider.buildRuntimeSettings)
+ quarkusConfigUnsupportedProperties.forEach(allSettings::putIfAbsent);
+
+ var databaseOrmCompatibilityVersion = puDefinition.getDatabaseOrmCompatibilityVersion();
+ Map appliedDatabaseOrmCompatibilitySettings = new HashMap<>();
+ for (Map.Entry entry : databaseOrmCompatibilityVersion.settings(puDefinition.getDbKind()).entrySet()) {
+ // Not using putIfAbsent() because that would be ambiguous in case of null values
+ if (!allSettings.containsKey(entry.getKey())) {
+ appliedDatabaseOrmCompatibilitySettings.put(entry.getKey(), entry.getValue());
+ }
+ }
+ allSettings.putAll(appliedDatabaseOrmCompatibilitySettings);
+
+ // We keep a separate copy of settings coming from Quarkus config,
+ // so that we can more easily differentiate between
+ // properties coming from Quarkus and "unsupported" properties
+ // on startup (see io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider.buildRuntimeSettings)
+ return new BuildTimeSettings(quarkusConfigSettings, databaseOrmCompatibilityVersion,
+ appliedDatabaseOrmCompatibilitySettings, allSettings);
+ }
+
/**
* Simplified copy of
* org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl#mergeSettings(org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor,
diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java
index 2b6b1c4901216..48ecf9765d51b 100644
--- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java
+++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java
@@ -8,6 +8,7 @@
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
import io.quarkus.hibernate.orm.runtime.boot.xml.RecordableXmlMapping;
+import io.quarkus.hibernate.orm.runtime.config.DatabaseOrmCompatibilityVersion;
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticDescriptor;
import io.quarkus.hibernate.orm.runtime.migration.MultiTenancyStrategy;
import io.quarkus.runtime.annotations.RecordableConstructor;
@@ -20,27 +21,34 @@ public final class QuarkusPersistenceUnitDefinition {
private final RuntimePersistenceUnitDescriptor actualHibernateDescriptor;
private final Optional dataSource;
+ private final Optional dbKind;
private final MultiTenancyStrategy multitenancyStrategy;
private final List xmlMappings;
private final boolean isReactive;
private final boolean fromPersistenceXml;
private final List integrationStaticDescriptors;
private final Map quarkusConfigUnsupportedProperties;
+ private final DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion;
public QuarkusPersistenceUnitDefinition(PersistenceUnitDescriptor persistenceUnitDescriptor,
- String configurationName, Optional dataSource,
+ String configurationName, Optional dataSource, Optional dbKind,
MultiTenancyStrategy multitenancyStrategy, List xmlMappings,
Map quarkusConfigUnsupportedProperties,
+ DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion,
boolean isReactive, boolean fromPersistenceXml,
List integrationStaticDescriptors) {
Objects.requireNonNull(persistenceUnitDescriptor);
+ Objects.requireNonNull(dataSource);
+ Objects.requireNonNull(dbKind);
Objects.requireNonNull(multitenancyStrategy);
this.actualHibernateDescriptor = RuntimePersistenceUnitDescriptor.validateAndReadFrom(persistenceUnitDescriptor,
configurationName);
this.dataSource = dataSource;
+ this.dbKind = dbKind;
this.multitenancyStrategy = multitenancyStrategy;
this.xmlMappings = xmlMappings;
this.quarkusConfigUnsupportedProperties = quarkusConfigUnsupportedProperties;
+ this.databaseOrmCompatibilityVersion = databaseOrmCompatibilityVersion;
this.isReactive = isReactive;
this.fromPersistenceXml = fromPersistenceXml;
this.integrationStaticDescriptors = integrationStaticDescriptors;
@@ -48,21 +56,25 @@ public QuarkusPersistenceUnitDefinition(PersistenceUnitDescriptor persistenceUni
@RecordableConstructor
public QuarkusPersistenceUnitDefinition(RuntimePersistenceUnitDescriptor actualHibernateDescriptor,
- Optional dataSource,
+ Optional dataSource, Optional dbKind,
MultiTenancyStrategy multitenancyStrategy,
List xmlMappings,
Map quarkusConfigUnsupportedProperties,
+ DatabaseOrmCompatibilityVersion databaseOrmCompatibilityVersion,
boolean reactive,
boolean fromPersistenceXml,
List integrationStaticDescriptors) {
Objects.requireNonNull(actualHibernateDescriptor);
Objects.requireNonNull(dataSource);
+ Objects.requireNonNull(dbKind);
Objects.requireNonNull(multitenancyStrategy);
this.actualHibernateDescriptor = actualHibernateDescriptor;
this.dataSource = dataSource;
+ this.dbKind = dbKind;
this.multitenancyStrategy = multitenancyStrategy;
this.xmlMappings = xmlMappings;
this.quarkusConfigUnsupportedProperties = quarkusConfigUnsupportedProperties;
+ this.databaseOrmCompatibilityVersion = databaseOrmCompatibilityVersion;
this.isReactive = reactive;
this.fromPersistenceXml = fromPersistenceXml;
this.integrationStaticDescriptors = integrationStaticDescriptors;
@@ -80,6 +92,10 @@ public Optional getDataSource() {
return dataSource;
}
+ public Optional getDbKind() {
+ return dbKind;
+ }
+
public MultiTenancyStrategy getMultitenancyStrategy() {
return multitenancyStrategy;
}
@@ -105,4 +121,7 @@ public Map getQuarkusConfigUnsupportedProperties() {
return quarkusConfigUnsupportedProperties;
}
+ public DatabaseOrmCompatibilityVersion getDatabaseOrmCompatibilityVersion() {
+ return databaseOrmCompatibilityVersion;
+ }
}
diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/config/DatabaseOrmCompatibilityVersion.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/config/DatabaseOrmCompatibilityVersion.java
new file mode 100644
index 0000000000000..e1fed5059b927
--- /dev/null
+++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/config/DatabaseOrmCompatibilityVersion.java
@@ -0,0 +1,85 @@
+package io.quarkus.hibernate.orm.runtime.config;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.hibernate.cfg.AvailableSettings;
+
+import io.quarkus.datasource.common.runtime.DatabaseKind;
+
+public enum DatabaseOrmCompatibilityVersion {
+ V5_6("5.6") {
+ @Override
+ public Map settings(Optional dbKind) {
+ Map result = new HashMap<>(Map.of(
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#implicit-identifier-sequence-and-table-name
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#id-sequence-defaults
+ AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY, "legacy",
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#duration-mapping-changes
+ AvailableSettings.PREFERRED_DURATION_JDBC_TYPE, "BIGINT",
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#instant-mapping-changes
+ AvailableSettings.PREFERRED_INSTANT_JDBC_TYPE, "TIMESTAMP",
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#basic-arraycollection-mapping
+ // Not changing this for now as there's no setting and affected users should be rare, and they can fix their code rather easily.
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#enum-mapping-changes
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#datatype-for-enums
+ // Not changing this because we cannot:
+ // there is no setting for this, so the schema will be incompatible.
+ // Runtime (queries, persisting) should continue to work, though.
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#timezone-and-offset-storage
+ AvailableSettings.TIMEZONE_DEFAULT_STORAGE, "NORMALIZE"));
+
+ if (dbKind.isPresent() && !usedToSupportUuid(dbKind.get())) {
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#uuid-mapping-changes
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#uuid-mapping-changes-on-mariadb
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#uuid-mapping-changes-on-sql-server
+ result.put(AvailableSettings.PREFERRED_UUID_JDBC_TYPE, "BINARY");
+ }
+
+ return result;
+ }
+ },
+ LATEST("latest") {
+ @Override
+ public Map settings(Optional dbKind) {
+ // Nothing to add
+ return Map.of();
+ }
+ };
+
+ private static boolean usedToSupportUuid(String dbKind) {
+ // As far as I can tell, only the PostgreSQL dialect used to support a native UUID type in ORM 5.x.
+ return DatabaseKind.isPostgreSQL(dbKind);
+ }
+
+ public final String externalRepresentation;
+
+ DatabaseOrmCompatibilityVersion(String externalRepresentation) {
+ this.externalRepresentation = externalRepresentation;
+ }
+
+ public abstract Map settings(Optional dbKind);
+
+ public static class Converter
+ implements org.eclipse.microprofile.config.spi.Converter {
+ @Override
+ public DatabaseOrmCompatibilityVersion convert(String value) {
+ final String normalizedValue = value.trim().toLowerCase(Locale.ROOT);
+ for (DatabaseOrmCompatibilityVersion candidate : values()) {
+ if (candidate.externalRepresentation.equals(normalizedValue)) {
+ return candidate;
+ }
+ }
+ throw new IllegalArgumentException(String.format(Locale.ROOT,
+ "Invalid ORM compatibility version: %1$s. Valid versions are: %2$s.",
+ value,
+ Arrays.stream(values())
+ .map(v -> v.externalRepresentation)
+ .collect(Collectors.toList())));
+ }
+ }
+}
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 e3e2effa6887f..aabf0d4426ce0 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
@@ -163,9 +163,10 @@ public void buildReactivePersistenceUnit(
// - we don't support starting Hibernate Reactive from a persistence.xml
// - we don't support Hibernate Envers with Hibernate Reactive
persistenceUnitDescriptors.produce(new PersistenceUnitDescriptorBuildItem(reactivePU,
- PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME,
+ PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, dbKindOptional,
jpaModel.getXmlMappings(reactivePU.getName()),
persistenceUnitConfig.unsupportedProperties,
+ hibernateOrmConfig.databaseOrmCompatibilityVersion,
true, false));
}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/README.md b/integration-tests/hibernate-orm-compatibility-5.6/README.md
new file mode 100644
index 0000000000000..dee5471439527
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/README.md
@@ -0,0 +1,34 @@
+# Hibernate ORM 5.6 database compatibility tests
+
+## What is this?
+
+These module test that Quarkus can indeed work with a database created by Hibernate ORM 5.6
+when the following property is set:
+
+```properties
+quarkus.hibernate-orm.database.orm-compatibility.version = 5.6
+```
+
+## How does it work?
+
+The tests need to execute on a database whose schema and data
+was initialized with Hibernate ORM 5.6.
+
+Everything is already set up to restore a dump on startup.
+
+## How to update the tests?
+
+If you add new tests and those changes require new entity mappings and/or data,
+make sure to update the project `database-generator` accordingly
+(same entity mapping as in your tests, in particular).
+This project depends on Quarkus 2 and is used to generate a database.
+
+Then, to update the dump, run `./update-dump.sh` from each DB directory (`mariadb`, `postgresql`, ...).
+This will start a container, generate the database, and update the dump in `src/test/resources`.
+
+## Why is `database-generator` not part of the build?
+
+Because:
+
+1. It doesn't need to. This project is only meant to be used to update dumps.
+2. It depends on Quarkus 2, so adding it to the build would pollute the local Maven repository unnecessarily.
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/database-generator/pom.xml b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/pom.xml
new file mode 100644
index 0000000000000..d6206acb24038
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/pom.xml
@@ -0,0 +1,121 @@
+
+
+ 4.0.0
+ io.quarkus
+ quarkus-integration-test-hibernate-orm-compatibility-5.6-database-generator
+ 1.0.0-SNAPSHOT
+
+ 3.10.1
+ 17
+ UTF-8
+ UTF-8
+ quarkus-bom
+ io.quarkus.platform
+ 2.16.3.Final
+ true
+ 3.0.0-M7
+
+
+
+
+ ${quarkus.platform.group-id}
+ ${quarkus.platform.artifact-id}
+ ${quarkus.platform.version}
+ pom
+ import
+
+
+
+
+
+ io.quarkus
+ quarkus-hibernate-orm
+
+
+ io.quarkus
+ quarkus-jdbc-mariadb
+
+
+ io.quarkus
+ quarkus-jdbc-postgresql
+
+
+ io.quarkus
+ quarkus-arc
+
+
+
+
+
+ ${quarkus.platform.group-id}
+ quarkus-maven-plugin
+ ${quarkus.platform.version}
+ true
+
+
+
+ build
+ generate-code
+ generate-code-tests
+
+
+
+
+
+ maven-compiler-plugin
+ ${compiler-plugin.version}
+
+
+ -parameters
+
+
+
+
+ maven-surefire-plugin
+ ${surefire-plugin.version}
+
+
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+ maven-failsafe-plugin
+ ${surefire-plugin.version}
+
+
+
+ integration-test
+ verify
+
+
+
+ ${project.build.directory}/${project.build.finalName}-runner
+
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+
+
+
+
+
+ native
+
+
+ native
+
+
+
+ false
+ native
+
+
+
+
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/Main.java b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/Main.java
new file mode 100644
index 0000000000000..417babc8a9d43
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/Main.java
@@ -0,0 +1,96 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+
+import io.quarkus.narayana.jta.QuarkusTransaction;
+import io.quarkus.runtime.Quarkus;
+import io.quarkus.runtime.QuarkusApplication;
+import io.quarkus.runtime.annotations.QuarkusMain;
+
+@QuarkusMain
+public class Main {
+ public static void main(String... args) {
+ System.out.println("Initializing schema...");
+ Quarkus.run(QuarkusMain.class, args);
+ }
+
+ static class QuarkusMain implements QuarkusApplication {
+ @Inject
+ EntityManager em;
+
+ @Override
+ public int run(String... args) {
+ System.out.println("Initializing data...");
+ MyEntity createdEntity = QuarkusTransaction.requiringNew().call(() -> {
+ var entity = new MyEntity();
+ entity.duration = Duration.of(59, ChronoUnit.SECONDS);
+ entity.uuid = UUID.fromString("f49c6ba8-8d7f-417a-a255-d594dddf729f");
+ entity.instant = Instant.parse("2018-01-01T10:58:30.00Z");
+ entity.offsetDateTime = LocalDateTime.of(2018, 1, 1, 12, 58, 30, 0)
+ .atOffset(ZoneOffset.ofHours(2));
+ entity.zonedDateTime = LocalDateTime.of(2018, 1, 1, 12, 58, 30, 0)
+ .atZone(ZoneId.of("Africa/Cairo" /* UTC+2 */));
+ entity.intArray = new int[] { 0, 1, 42 };
+ entity.stringList = new ArrayList<>(List.of("one", "two"));
+ entity.myEnum = MyEnum.VALUE2;
+ em.persist(entity);
+
+ // Create more than one entity of each type,
+ // so that we avoid the (uninteresting) edge case in sequence optimizers
+ // where only 1 entity was created and the optimizer is just about to start another pool.
+ em.persist(new MyEntity());
+ em.persist(new MyEntityWithGenericGeneratorAndDefaultAllocationSize());
+ em.persist(new MyEntityWithGenericGeneratorAndDefaultAllocationSize());
+ em.persist(new MyEntityWithSequenceGeneratorAndDefaultAllocationSize());
+ em.persist(new MyEntityWithSequenceGeneratorAndDefaultAllocationSize());
+
+ return entity;
+ });
+
+ System.out.println("Checking data...");
+ // Check that Hibernate ORM 5 used to load the values we're going to expect in compatibility tests
+ QuarkusTransaction.requiringNew().run(() -> {
+ checkEqual(1L, createdEntity.id);
+ var loadedEntity = em.find(MyEntity.class, createdEntity.id);
+ checkEqual(createdEntity.duration, loadedEntity.duration);
+ checkEqual(createdEntity.uuid, loadedEntity.uuid);
+ checkEqual(createdEntity.instant, loadedEntity.instant);
+ checkEqual(createdEntity.offsetDateTime.atZoneSameInstant(ZoneId.systemDefault()).toOffsetDateTime(),
+ loadedEntity.offsetDateTime);
+ checkEqual(createdEntity.zonedDateTime.withZoneSameInstant(ZoneId.systemDefault()), loadedEntity.zonedDateTime);
+ checkEqual(createdEntity.intArray, loadedEntity.intArray);
+ checkEqual(createdEntity.stringList, loadedEntity.stringList);
+ checkEqual(createdEntity.myEnum, loadedEntity.myEnum);
+ });
+
+ System.out.println("Done.");
+ return 0;
+ }
+
+ private void checkEqual(T expected, T actual) {
+ if (!Objects.equals(expected, actual)) {
+ throw new AssertionError("Not equal; expected: " + expected + ", actual: " + actual);
+ }
+ }
+
+ private void checkEqual(int[] expected, int[] actual) {
+ if (!Arrays.equals(expected, actual)) {
+ throw new AssertionError("Not equal; expected: " + Arrays.toString(expected)
+ + ", actual: " + Arrays.toString(actual));
+ }
+ }
+ }
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntity.java b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntity.java
new file mode 100644
index 0000000000000..b4f459421bc1a
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntity.java
@@ -0,0 +1,39 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@Entity
+public class MyEntity {
+ @Id
+ @GeneratedValue
+ public Long id;
+
+ public Duration duration;
+
+ public UUID uuid;
+
+ public Instant instant;
+
+ public OffsetDateTime offsetDateTime;
+
+ public ZonedDateTime zonedDateTime;
+
+ public int[] intArray;
+
+ public ArrayList stringList;
+
+ @Enumerated(EnumType.ORDINAL)
+ public MyEnum myEnum;
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithGenericGeneratorAndDefaultAllocationSize.java b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithGenericGeneratorAndDefaultAllocationSize.java
new file mode 100644
index 0000000000000..80d275fc7d90c
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithGenericGeneratorAndDefaultAllocationSize.java
@@ -0,0 +1,17 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.hibernate.annotations.Parameter;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+@Entity(name = "myentity_gengendefallocsize")
+public class MyEntityWithGenericGeneratorAndDefaultAllocationSize {
+ @Id
+ @GeneratedValue(generator = "gengendefallocsize")
+ @GenericGenerator(name = "gengendefallocsize", strategy = "sequence")
+ public Long id;
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithSequenceGeneratorAndDefaultAllocationSize.java b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithSequenceGeneratorAndDefaultAllocationSize.java
new file mode 100644
index 0000000000000..82b47e2ef4675
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithSequenceGeneratorAndDefaultAllocationSize.java
@@ -0,0 +1,15 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.SequenceGenerator;
+
+@Entity(name = "myentity_seqgendefallocsize")
+public class MyEntityWithSequenceGeneratorAndDefaultAllocationSize {
+ @Id
+ @GeneratedValue(generator = "seqgendefallocsize")
+ @SequenceGenerator(name = "seqgendefallocsize")
+ public Long id;
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEnum.java b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEnum.java
new file mode 100644
index 0000000000000..47d99905d0acf
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/java/io/quarkus/it/hibernate/compatibility/MyEnum.java
@@ -0,0 +1,7 @@
+package io.quarkus.it.hibernate.compatibility;
+
+public enum MyEnum {
+ VALUE1,
+ VALUE2,
+ VALUE3
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/resources/application.properties b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/resources/application.properties
new file mode 100644
index 0000000000000..e783aed6cdb69
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/src/main/resources/application.properties
@@ -0,0 +1,3 @@
+# This is set from the commandline when building
+quarkus.datasource.db-kind=${db-kind}
+quarkus.hibernate-orm.database.generation=create
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/pom.xml b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/pom.xml
new file mode 100644
index 0000000000000..a4d95207936f8
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/pom.xml
@@ -0,0 +1,297 @@
+
+
+
+ quarkus-integration-tests-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../..
+
+ 4.0.0
+
+ quarkus-integration-test-hibernate-orm-compatibility-5.6-mariadb
+ Quarkus - Integration Tests - Hibernate ORM - Compatibility with databases meant for ORM 5.6 - MariaDB
+
+
+ jdbc:mariadb://localhost:3306/hibernate_orm_test
+
+ -Duser.timezone=Europe/Paris
+
+
+
+
+ io.quarkus
+ quarkus-hibernate-orm
+
+
+ io.quarkus
+ quarkus-jdbc-mariadb
+
+
+ io.quarkus
+ quarkus-flyway
+
+
+ org.flywaydb
+ flyway-mysql
+
+
+ io.quarkus
+ quarkus-resteasy-reactive
+
+
+ io.quarkus
+ quarkus-resteasy-reactive-jackson
+
+
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+
+ io.quarkus
+ quarkus-flyway-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-hibernate-orm-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-jdbc-mariadb-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-resteasy-reactive-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-resteasy-reactive-jackson-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+ maven-surefire-plugin
+
+ true
+
+
+
+ maven-failsafe-plugin
+
+ true
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+
+ build
+
+
+
+
+
+
+
+
+
+ test-mariadb
+
+
+ test-containers
+
+
+
+
+
+ maven-surefire-plugin
+
+ false
+
+
+
+ maven-failsafe-plugin
+
+ false
+
+
+
+
+
+
+
+ docker-mariadb
+
+
+ start-containers
+
+
+
+ jdbc:mariadb://localhost:3308/hibernate_orm_test
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+
+
+ ${mariadb.image}
+ quarkus-test-mariadb
+
+ ${mariadb.image}
+
+
+ 5s
+ 3s
+ 5s
+ 5
+
+
+ mysqladmin ping -h localhost -u root -psecret|| exit 1
+
+
+
+
+
+ 3308:3306
+
+
+ hibernate_orm_test
+ hibernate_orm_test
+ hibernate_orm_test
+ true
+
+
+ MariaDB:
+ default
+ cyan
+
+
+ /var/lib/mysql
+
+ 20000
+ true
+
+
+
+
+
+ true
+
+
+
+ docker-start
+ compile
+
+ stop
+ build
+ start
+
+
+
+ docker-stop
+ post-integration-test
+
+ stop
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+
+ docker-prune
+ generate-resources
+
+ exec
+
+
+ ${docker-prune.location}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestResource.java b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestResource.java
new file mode 100644
index 0000000000000..6daf149d2c933
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestResource.java
@@ -0,0 +1,51 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.persistence.EntityManager;
+import jakarta.transaction.Transactional;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+
+import org.jboss.resteasy.reactive.RestPath;
+
+@ApplicationScoped
+@Produces("application/json")
+@Consumes("application/json")
+@Path("/compatibility")
+@Transactional
+public class CompatibilityTestResource {
+ @Inject
+ EntityManager em;
+
+ @GET
+ @Path("/{id}")
+ public MyEntity find(@RestPath Long id) {
+ return em.find(MyEntity.class, id);
+ }
+
+ @POST
+ public MyEntity create(MyEntity entity) {
+ em.persist(entity);
+ return entity;
+ }
+
+ @POST
+ @Path("/genericgenerator")
+ public MyEntityWithGenericGeneratorAndDefaultAllocationSize create(
+ MyEntityWithGenericGeneratorAndDefaultAllocationSize entity) {
+ em.persist(entity);
+ return entity;
+ }
+
+ @POST
+ @Path("/sequencegenerator")
+ public MyEntityWithSequenceGeneratorAndDefaultAllocationSize create(
+ MyEntityWithSequenceGeneratorAndDefaultAllocationSize entity) {
+ em.persist(entity);
+ return entity;
+ }
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntity.java b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntity.java
new file mode 100644
index 0000000000000..8d1ca185bba5e
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntity.java
@@ -0,0 +1,48 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.UUID;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
+
+@Entity
+public class MyEntity {
+ @Id
+ @GeneratedValue
+ public Long id;
+
+ public Duration duration;
+
+ public UUID uuid;
+
+ public Instant instant;
+
+ public OffsetDateTime offsetDateTime;
+
+ public ZonedDateTime zonedDateTime;
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#basic-arraycollection-mapping
+ // This mapping change is required because Quarkus cannot fix this through settings.
+ @JdbcTypeCode(SqlTypes.VARBINARY)
+ public int[] intArray;
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#basic-arraycollection-mapping
+ // This mapping change is required because Quarkus cannot fix this through settings.
+ @JdbcTypeCode(SqlTypes.VARBINARY)
+ public ArrayList stringList;
+
+ @Enumerated(EnumType.ORDINAL)
+ public MyEnum myEnum;
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithGenericGeneratorAndDefaultAllocationSize.java b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithGenericGeneratorAndDefaultAllocationSize.java
new file mode 100644
index 0000000000000..64caf2a628045
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithGenericGeneratorAndDefaultAllocationSize.java
@@ -0,0 +1,16 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+
+import org.hibernate.annotations.GenericGenerator;
+
+@Entity(name = "myentity_gengendefallocsize")
+public class MyEntityWithGenericGeneratorAndDefaultAllocationSize {
+ @Id
+ @GeneratedValue(generator = "gengendefallocsize")
+ @GenericGenerator(name = "gengendefallocsize", strategy = "sequence")
+ public Long id;
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithSequenceGeneratorAndDefaultAllocationSize.java b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithSequenceGeneratorAndDefaultAllocationSize.java
new file mode 100644
index 0000000000000..e8bf345e8e6f7
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithSequenceGeneratorAndDefaultAllocationSize.java
@@ -0,0 +1,15 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.SequenceGenerator;
+
+@Entity(name = "myentity_seqgendefallocsize")
+public class MyEntityWithSequenceGeneratorAndDefaultAllocationSize {
+ @Id
+ @GeneratedValue(generator = "seqgendefallocsize")
+ @SequenceGenerator(name = "seqgendefallocsize")
+ public Long id;
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEnum.java b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEnum.java
new file mode 100644
index 0000000000000..47d99905d0acf
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/java/io/quarkus/it/hibernate/compatibility/MyEnum.java
@@ -0,0 +1,7 @@
+package io.quarkus.it.hibernate.compatibility;
+
+public enum MyEnum {
+ VALUE1,
+ VALUE2,
+ VALUE3
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/resources/application.properties b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/resources/application.properties
new file mode 100644
index 0000000000000..a9a64cd31cebc
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/main/resources/application.properties
@@ -0,0 +1,16 @@
+quarkus.datasource.username=hibernate_orm_test
+quarkus.datasource.password=hibernate_orm_test
+quarkus.datasource.jdbc.url=${mariadb.url}
+quarkus.datasource.jdbc.max-size=8
+
+# On startup, restore the dump of a database meant for ORM 5.6
+quarkus.flyway.migrate-at-start=true
+# https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#enum-mapping-changes
+# https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#datatype-for-enums
+# The schema changed for enums and there is nothing we can do about it,
+# so we're cannot pass schema validation.
+quarkus.hibernate-orm.database.generation=none
+
+# Configure Hibernate ORM for compatibility with ORM 5.6
+quarkus.hibernate-orm.database.orm-compatibility.version=5.6
+
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTest.java b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTest.java
new file mode 100644
index 0000000000000..6bf66a479147b
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTest.java
@@ -0,0 +1,163 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import static io.restassured.RestAssured.given;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import jakarta.ws.rs.core.Response.Status;
+
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class CompatibilityTest {
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#implicit-identifier-sequence-and-table-name
+ @Test
+ @Order(1)
+ public void sequence_defaultGenerator() {
+ var entity = new MyEntity();
+ MyEntity createdEntity = given()
+ .body(entity).contentType("application/json")
+ .when().post("/compatibility/").then()
+ .assertThat().statusCode(is(Status.OK.getStatusCode()))
+ .extract().as(MyEntity.class);
+ assertThat(createdEntity.id).isEqualTo(3L);
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#id-sequence-defaults
+ @Test
+ @Order(2)
+ public void sequence_genericGenerator_defaultAllocation() {
+ var entity = new MyEntityWithGenericGeneratorAndDefaultAllocationSize();
+ MyEntityWithGenericGeneratorAndDefaultAllocationSize createdEntity = given()
+ .body(entity).contentType("application/json")
+ .when().post("/compatibility/genericgenerator").then()
+ .assertThat().statusCode(is(Status.OK.getStatusCode()))
+ .extract().as(MyEntityWithGenericGeneratorAndDefaultAllocationSize.class);
+ assertThat(createdEntity.id).isEqualTo(3L);
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#id-sequence-defaults
+ @Test
+ @Order(2)
+ public void sequence_sequenceGenerator_defaultAllocation() {
+ var entity = new MyEntityWithSequenceGeneratorAndDefaultAllocationSize();
+ MyEntityWithSequenceGeneratorAndDefaultAllocationSize createdEntity = given()
+ .body(entity).contentType("application/json")
+ .when().post("/compatibility/sequencegenerator").then()
+ .assertThat().statusCode(is(Status.OK.getStatusCode()))
+ .extract().as(MyEntityWithSequenceGeneratorAndDefaultAllocationSize.class);
+ // Sequence generators defined through @SequenceGenerator have always defaulted to an allocation size of 50.
+ // Since we've created 2 entities in Hibernate 5, we should be starting the second pool here, starting at 52.
+ assertThat(createdEntity.id).isEqualTo(52L);
+ }
+
+ // Just check that persisting with the old schema and new application does not throw any exception
+ @Test
+ @Order(4) // So that we can assert the generated ID in sequence*()
+ public void persistUsingOldSchema() {
+ var entity = new MyEntity();
+ entity.duration = Duration.of(59, ChronoUnit.SECONDS);
+ entity.uuid = UUID.fromString("f49c6ba8-8d7f-417a-a255-d594dddf729f");
+ entity.instant = Instant.parse("2018-01-01T10:58:30.00Z");
+ entity.intArray = new int[] { 0, 1, 42 };
+ entity.offsetDateTime = LocalDateTime.of(2018, 1, 1, 12, 58, 30, 0)
+ .atOffset(ZoneOffset.ofHours(2));
+ entity.zonedDateTime = LocalDateTime.of(2018, 1, 1, 12, 58, 30, 0)
+ .atZone(ZoneId.of("Africa/Cairo" /* UTC+2 */));
+ entity.stringList = new ArrayList<>(List.of("one", "two"));
+ entity.myEnum = MyEnum.VALUE2;
+ given()
+ .body(entity).contentType("application/json")
+ .when().post("/compatibility/").then()
+ .assertThat().statusCode(is(Status.OK.getStatusCode()));
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#duration-mapping-changes
+ @Test
+ public void duration() {
+ assertThat(findOld().duration).isEqualTo(Duration.of(59, ChronoUnit.SECONDS));
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#uuid-mapping-changes
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#uuid-mapping-changes-on-mariadb
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#uuid-mapping-changes-on-sql-server
+ @Test
+ public void uuid() {
+ assertThat(findOld().uuid).isEqualTo(UUID.fromString("f49c6ba8-8d7f-417a-a255-d594dddf729f"));
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#instant-mapping-changes
+ @Test
+ public void instant() {
+ assertThat(findOld().instant).isEqualTo(Instant.parse("2018-01-01T10:58:30.00Z"));
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#timezone-and-offset-storage
+ @Test
+ public void offsetDateTime() {
+ assertThat(findOld().offsetDateTime)
+ .isEqualTo(LocalDateTime.of(2018, 1, 1, 12, 58, 30, 0)
+ .atOffset(ZoneOffset.ofHours(2))
+ // Hibernate ORM 5 used to normalize these values to the JVM TZ
+ .atZoneSameInstant(ZoneId.systemDefault()).toOffsetDateTime());
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#timezone-and-offset-storage
+ @Test
+ public void zonedDateTime() {
+ assertThat(findOld().zonedDateTime)
+ .isEqualTo(LocalDateTime.of(2018, 1, 1, 12, 58, 30, 0)
+ .atZone(ZoneId.of("Africa/Cairo" /* UTC+2 */))
+ // Hibernate ORM 5 used to normalize these values to the JVM TZ
+ .withZoneSameInstant(ZoneId.systemDefault()));
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#basic-arraycollection-mapping
+ // Note this is not fixed automatically by Quarkus and requires new annotations on the entity (see entity class).
+ @Test
+ public void array() {
+ assertThat(findOld().intArray).isEqualTo(new int[] { 0, 1, 42 });
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#basic-arraycollection-mapping
+ // Note this is not fixed automatically by Quarkus and requires new annotations on the entity (see entity class).
+ @Test
+ public void list() {
+ assertThat(findOld().stringList).isEqualTo(new ArrayList<>(List.of("one", "two")));
+ }
+
+ @Test
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#enum-mapping-changes
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#datatype-for-enums
+ public void enum_() {
+ assertThat(findOld().myEnum).isEqualTo(MyEnum.VALUE2);
+ }
+
+ private static MyEntity findOld() {
+ return find(1L);
+ }
+
+ private static MyEntity find(long id) {
+ return given().when().get("/compatibility/{id}", id).then()
+ .assertThat().statusCode(is(Status.OK.getStatusCode()))
+ .extract().as(MyEntity.class);
+ }
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestInGraalITCase.java b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestInGraalITCase.java
new file mode 100644
index 0000000000000..4564e10519e9a
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestInGraalITCase.java
@@ -0,0 +1,8 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+public class CompatibilityTestInGraalITCase extends CompatibilityTest {
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/resources/db/migration/V1.0.0__orm5-6.sql b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/resources/db/migration/V1.0.0__orm5-6.sql
new file mode 100644
index 0000000000000..af0edfd88353f
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/resources/db/migration/V1.0.0__orm5-6.sql
@@ -0,0 +1,38 @@
+CREATE SEQUENCE `gengendefallocsize` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=InnoDB;
+SELECT SETVAL(`gengendefallocsize`, 1001, 0);
+CREATE SEQUENCE `hibernate_sequence` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=InnoDB;
+SELECT SETVAL(`hibernate_sequence`, 1001, 0);
+CREATE SEQUENCE `seqgendefallocsize` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 50 cache 1000 nocycle ENGINE=InnoDB;
+SELECT SETVAL(`seqgendefallocsize`, 50001, 0);
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `MyEntity` (
+ `id` bigint(20) NOT NULL,
+ `duration` bigint(20) DEFAULT NULL,
+ `instant` datetime(6) DEFAULT NULL,
+ `intArray` tinyblob DEFAULT NULL,
+ `myEnum` int(11) DEFAULT NULL,
+ `offsetDateTime` datetime(6) DEFAULT NULL,
+ `stringList` tinyblob DEFAULT NULL,
+ `uuid` binary(255) DEFAULT NULL,
+ `zonedDateTime` datetime(6) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `MyEntity` VALUES (1,59000000000,'2018-01-01 11:58:30.000000',0xACED0005757200025B494DBA602676EAB2A502000078700000000300000000000000010000002A,1,'2018-01-01 11:58:30.000000',0xACED0005737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D03000149000473697A657870000000027704000000027400036F6E6574000374776F78,0xF49C6BA88D7F417AA255D594DDDF729F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,'2018-01-01 11:58:30.000000'),(2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `myentity_gengendefallocsize` (
+ `id` bigint(20) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `myentity_gengendefallocsize` VALUES (1),(2);
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `myentity_seqgendefallocsize` (
+ `id` bigint(20) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `myentity_seqgendefallocsize` VALUES (1),(2);
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/resources/db/migration/V1.0.1__reset-sequences.sql b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/resources/db/migration/V1.0.1__reset-sequences.sql
new file mode 100644
index 0000000000000..0c80a34f37cad
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/src/test/resources/db/migration/V1.0.1__reset-sequences.sql
@@ -0,0 +1,18 @@
+-- MariaDB dumps lead to weird values in sequences because of caching mechanisms.
+-- => Reset sequences to the value they would have had, had we not dumped and restored the database.
+
+ALTER SEQUENCE `hibernate_sequence` restart;
+-- We created exactly two entities
+SELECT nextval(`hibernate_sequence`);
+SELECT nextval(`hibernate_sequence`);
+
+ALTER SEQUENCE `gengendefallocsize` restart;
+-- We created exactly two entities
+SELECT nextval(`gengendefallocsize`);
+SELECT nextval(`gengendefallocsize`);
+
+ALTER SEQUENCE `seqgendefallocsize` restart;
+-- We created exactly two entities
+-- This sequence uses a pooled optimizer, but the first two entities still require two calls to nextval()
+SELECT nextval(`seqgendefallocsize`);
+SELECT nextval(`seqgendefallocsize`);
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/mariadb/update-dump.sh b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/update-dump.sh
new file mode 100755
index 0000000000000..442c72059b0b6
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/mariadb/update-dump.sh
@@ -0,0 +1,30 @@
+#!/bin/bash -e
+
+MVN=../../../mvnw
+DB_CONTAINER_NAME=mariadb-database-gen
+# These should be the same as the username/password used by docker-maven-plugin in ./pom.xml
+DB_NAME=hibernate_orm_test
+DB_PORT=3308
+DB_USER=hibernate_orm_test
+DB_PASSWORD=hibernate_orm_test
+DUMP_LOCATION=src/test/resources/db/migration/V1.0.0__orm5-6.sql
+
+# Start container
+$MVN docker:stop docker:build docker:start -Dstart-containers -Ddocker.containerNamePattern=$DB_CONTAINER_NAME
+trap '$MVN docker:stop -Dstart-containers -Ddocker.containerNamePattern=$DB_CONTAINER_NAME' EXIT
+
+# Generate database
+$MVN -f ../database-generator clean install -Ddb-kind=mariadb
+# We use features in Hibernate ORM that are timezone-sensitive, e.g. ZoneOffsetDateTime normalization
+TZ=Europe/Paris
+QUARKUS_DATASOURCE_JDBC_URL="jdbc:mariadb://localhost:$DB_PORT/$DB_NAME" \
+QUARKUS_DATASOURCE_USERNAME="$DB_USER" \
+QUARKUS_DATASOURCE_PASSWORD="$DB_PASSWORD" \
+ java -jar ../database-generator/target/quarkus-app/quarkus-run.jar
+
+# Update the dump
+# https://stackoverflow.com/a/32611542/6692043
+echo "Updating dump at '$DUMP_LOCATION'"
+docker exec "$DB_CONTAINER_NAME" \
+ sh -c 'exec mysqldump -hlocalhost -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" --compact --hex-blob' \
+ > "$DUMP_LOCATION"
\ No newline at end of file
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/pom.xml b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/pom.xml
new file mode 100644
index 0000000000000..8d53080e47976
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/pom.xml
@@ -0,0 +1,277 @@
+
+
+
+ quarkus-integration-tests-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../..
+
+ 4.0.0
+
+ quarkus-integration-test-hibernate-orm-compatibility-5.6-postgresql
+ Quarkus - Integration Tests - Hibernate ORM - Compatibility with databases meant for ORM 5.6 - PostgreSQL
+
+
+
+ -Duser.timezone=Europe/Paris
+
+
+
+
+ io.quarkus
+ quarkus-hibernate-orm
+
+
+ io.quarkus
+ quarkus-jdbc-postgresql
+
+
+ io.quarkus
+ quarkus-flyway
+
+
+ org.flywaydb
+ flyway-mysql
+
+
+ io.quarkus
+ quarkus-resteasy-reactive
+
+
+ io.quarkus
+ quarkus-resteasy-reactive-jackson
+
+
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+
+ io.quarkus
+ quarkus-flyway-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-hibernate-orm-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-jdbc-postgresql-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-resteasy-reactive-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-resteasy-reactive-jackson-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+ maven-surefire-plugin
+
+ true
+
+
+
+ maven-failsafe-plugin
+
+ true
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+
+ build
+
+
+
+
+
+
+
+
+
+ test-postgresql
+
+
+ test-containers
+
+
+
+
+
+ maven-surefire-plugin
+
+ false
+
+
+
+ maven-failsafe-plugin
+
+ false
+
+
+
+
+
+
+
+ docker-postgresql
+
+
+ start-containers
+
+
+
+ jdbc:postgresql://localhost:5431/hibernate_orm_test
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+
+
+ ${postgres.image}
+ postgresql
+
+
+ hibernate_orm_test
+ hibernate_orm_test
+ hibernate_orm_test
+
+
+ 5431:5432
+
+
+
+ mapped
+
+ 5432
+
+
+ 10000
+
+
+
+
+
+ true
+
+
+
+ docker-start
+ compile
+
+ stop
+ start
+
+
+
+ docker-stop
+ post-integration-test
+
+ stop
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+
+ docker-prune
+ generate-resources
+
+ exec
+
+
+ ${docker-prune.location}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestResource.java b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestResource.java
new file mode 100644
index 0000000000000..6daf149d2c933
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestResource.java
@@ -0,0 +1,51 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.persistence.EntityManager;
+import jakarta.transaction.Transactional;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+
+import org.jboss.resteasy.reactive.RestPath;
+
+@ApplicationScoped
+@Produces("application/json")
+@Consumes("application/json")
+@Path("/compatibility")
+@Transactional
+public class CompatibilityTestResource {
+ @Inject
+ EntityManager em;
+
+ @GET
+ @Path("/{id}")
+ public MyEntity find(@RestPath Long id) {
+ return em.find(MyEntity.class, id);
+ }
+
+ @POST
+ public MyEntity create(MyEntity entity) {
+ em.persist(entity);
+ return entity;
+ }
+
+ @POST
+ @Path("/genericgenerator")
+ public MyEntityWithGenericGeneratorAndDefaultAllocationSize create(
+ MyEntityWithGenericGeneratorAndDefaultAllocationSize entity) {
+ em.persist(entity);
+ return entity;
+ }
+
+ @POST
+ @Path("/sequencegenerator")
+ public MyEntityWithSequenceGeneratorAndDefaultAllocationSize create(
+ MyEntityWithSequenceGeneratorAndDefaultAllocationSize entity) {
+ em.persist(entity);
+ return entity;
+ }
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntity.java b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntity.java
new file mode 100644
index 0000000000000..8d1ca185bba5e
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntity.java
@@ -0,0 +1,48 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.UUID;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
+
+@Entity
+public class MyEntity {
+ @Id
+ @GeneratedValue
+ public Long id;
+
+ public Duration duration;
+
+ public UUID uuid;
+
+ public Instant instant;
+
+ public OffsetDateTime offsetDateTime;
+
+ public ZonedDateTime zonedDateTime;
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#basic-arraycollection-mapping
+ // This mapping change is required because Quarkus cannot fix this through settings.
+ @JdbcTypeCode(SqlTypes.VARBINARY)
+ public int[] intArray;
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#basic-arraycollection-mapping
+ // This mapping change is required because Quarkus cannot fix this through settings.
+ @JdbcTypeCode(SqlTypes.VARBINARY)
+ public ArrayList stringList;
+
+ @Enumerated(EnumType.ORDINAL)
+ public MyEnum myEnum;
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithGenericGeneratorAndDefaultAllocationSize.java b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithGenericGeneratorAndDefaultAllocationSize.java
new file mode 100644
index 0000000000000..64caf2a628045
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithGenericGeneratorAndDefaultAllocationSize.java
@@ -0,0 +1,16 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+
+import org.hibernate.annotations.GenericGenerator;
+
+@Entity(name = "myentity_gengendefallocsize")
+public class MyEntityWithGenericGeneratorAndDefaultAllocationSize {
+ @Id
+ @GeneratedValue(generator = "gengendefallocsize")
+ @GenericGenerator(name = "gengendefallocsize", strategy = "sequence")
+ public Long id;
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithSequenceGeneratorAndDefaultAllocationSize.java b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithSequenceGeneratorAndDefaultAllocationSize.java
new file mode 100644
index 0000000000000..e8bf345e8e6f7
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEntityWithSequenceGeneratorAndDefaultAllocationSize.java
@@ -0,0 +1,15 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.SequenceGenerator;
+
+@Entity(name = "myentity_seqgendefallocsize")
+public class MyEntityWithSequenceGeneratorAndDefaultAllocationSize {
+ @Id
+ @GeneratedValue(generator = "seqgendefallocsize")
+ @SequenceGenerator(name = "seqgendefallocsize")
+ public Long id;
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEnum.java b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEnum.java
new file mode 100644
index 0000000000000..47d99905d0acf
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/java/io/quarkus/it/hibernate/compatibility/MyEnum.java
@@ -0,0 +1,7 @@
+package io.quarkus.it.hibernate.compatibility;
+
+public enum MyEnum {
+ VALUE1,
+ VALUE2,
+ VALUE3
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/resources/application.properties b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/resources/application.properties
new file mode 100644
index 0000000000000..ef7cac5fdc786
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/main/resources/application.properties
@@ -0,0 +1,16 @@
+quarkus.datasource.username=hibernate_orm_test
+quarkus.datasource.password=hibernate_orm_test
+quarkus.datasource.jdbc.url=${postgres.url}
+quarkus.datasource.jdbc.max-size=8
+
+# On startup, restore the dump of a database meant for ORM 5.6
+quarkus.flyway.migrate-at-start=true
+# https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#enum-mapping-changes
+# https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#datatype-for-enums
+# The schema changed for enums and there is nothing we can do about it,
+# so we're cannot pass schema validation.
+quarkus.hibernate-orm.database.generation=none
+
+# Configure Hibernate ORM for compatibility with ORM 5.6
+quarkus.hibernate-orm.database.orm-compatibility.version=5.6
+
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTest.java b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTest.java
new file mode 100644
index 0000000000000..19fa1ec2e5a20
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTest.java
@@ -0,0 +1,163 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import static io.restassured.RestAssured.given;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import jakarta.ws.rs.core.Response.Status;
+
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class CompatibilityTest {
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#implicit-identifier-sequence-and-table-name
+ @Test
+ @Order(1)
+ public void sequence_defaultGenerator() {
+ var entity = new MyEntity();
+ MyEntity createdEntity = given()
+ .body(entity).contentType("application/json")
+ .when().post("/compatibility/").then()
+ .assertThat().statusCode(is(Status.OK.getStatusCode()))
+ .extract().as(MyEntity.class);
+ assertThat(createdEntity.id).isEqualTo(3);
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#id-sequence-defaults
+ @Test
+ @Order(2)
+ public void sequence_genericGenerator_defaultAllocation() {
+ var entity = new MyEntityWithGenericGeneratorAndDefaultAllocationSize();
+ MyEntityWithGenericGeneratorAndDefaultAllocationSize createdEntity = given()
+ .body(entity).contentType("application/json")
+ .when().post("/compatibility/genericgenerator").then()
+ .assertThat().statusCode(is(Status.OK.getStatusCode()))
+ .extract().as(MyEntityWithGenericGeneratorAndDefaultAllocationSize.class);
+ assertThat(createdEntity.id).isEqualTo(3);
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#id-sequence-defaults
+ @Test
+ @Order(3)
+ public void sequence_sequenceGenerator_defaultAllocation() {
+ var entity = new MyEntityWithSequenceGeneratorAndDefaultAllocationSize();
+ MyEntityWithSequenceGeneratorAndDefaultAllocationSize createdEntity = given()
+ .body(entity).contentType("application/json")
+ .when().post("/compatibility/sequencegenerator").then()
+ .assertThat().statusCode(is(Status.OK.getStatusCode()))
+ .extract().as(MyEntityWithSequenceGeneratorAndDefaultAllocationSize.class);
+ // Sequence generators defined through @SequenceGenerator have always defaulted to an allocation size of 50.
+ // Since we've created 2 entities in Hibernate 5, we should be starting the second pool here, starting at 52.
+ assertThat(createdEntity.id).isEqualTo(52L);
+ }
+
+ // Just check that persisting with the old schema and new application does not throw any exception
+ @Test
+ @Order(4) // So that we can assert the generated ID in sequence*()
+ public void persistUsingOldSchema() {
+ var entity = new MyEntity();
+ entity.duration = Duration.of(59, ChronoUnit.SECONDS);
+ entity.uuid = UUID.fromString("f49c6ba8-8d7f-417a-a255-d594dddf729f");
+ entity.instant = Instant.parse("2018-01-01T10:58:30.00Z");
+ entity.intArray = new int[] { 0, 1, 42 };
+ entity.offsetDateTime = LocalDateTime.of(2018, 1, 1, 12, 58, 30, 0)
+ .atOffset(ZoneOffset.ofHours(2));
+ entity.zonedDateTime = LocalDateTime.of(2018, 1, 1, 12, 58, 30, 0)
+ .atZone(ZoneId.of("Africa/Cairo" /* UTC+2 */));
+ entity.stringList = new ArrayList<>(List.of("one", "two"));
+ entity.myEnum = MyEnum.VALUE2;
+ given()
+ .body(entity).contentType("application/json")
+ .when().post("/compatibility/").then()
+ .assertThat().statusCode(is(Status.OK.getStatusCode()));
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#duration-mapping-changes
+ @Test
+ public void duration() {
+ assertThat(findOld().duration).isEqualTo(Duration.of(59, ChronoUnit.SECONDS));
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#uuid-mapping-changes
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#uuid-mapping-changes-on-mariadb
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#uuid-mapping-changes-on-sql-server
+ @Test
+ public void uuid() {
+ assertThat(findOld().uuid).isEqualTo(UUID.fromString("f49c6ba8-8d7f-417a-a255-d594dddf729f"));
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#instant-mapping-changes
+ @Test
+ public void instant() {
+ assertThat(findOld().instant).isEqualTo(Instant.parse("2018-01-01T10:58:30.00Z"));
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#timezone-and-offset-storage
+ @Test
+ public void offsetDateTime() {
+ assertThat(findOld().offsetDateTime)
+ .isEqualTo(LocalDateTime.of(2018, 1, 1, 12, 58, 30, 0)
+ .atOffset(ZoneOffset.ofHours(2))
+ // Hibernate ORM 5 used to normalize these values to the JVM TZ
+ .atZoneSameInstant(ZoneId.systemDefault()).toOffsetDateTime());
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#timezone-and-offset-storage
+ @Test
+ public void zonedDateTime() {
+ assertThat(findOld().zonedDateTime)
+ .isEqualTo(LocalDateTime.of(2018, 1, 1, 12, 58, 30, 0)
+ .atZone(ZoneId.of("Africa/Cairo" /* UTC+2 */))
+ // Hibernate ORM 5 used to normalize these values to the JVM TZ
+ .withZoneSameInstant(ZoneId.systemDefault()));
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#basic-arraycollection-mapping
+ // Note this is not fixed automatically by Quarkus and requires new annotations on the entity (see entity class).
+ @Test
+ public void array() {
+ assertThat(findOld().intArray).isEqualTo(new int[] { 0, 1, 42 });
+ }
+
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#basic-arraycollection-mapping
+ // Note this is not fixed automatically by Quarkus and requires new annotations on the entity (see entity class).
+ @Test
+ public void list() {
+ assertThat(findOld().stringList).isEqualTo(new ArrayList<>(List.of("one", "two")));
+ }
+
+ @Test
+ // https://github.com/hibernate/hibernate-orm/blob/6.1/migration-guide.adoc#enum-mapping-changes
+ // https://github.com/hibernate/hibernate-orm/blob/6.2/migration-guide.adoc#datatype-for-enums
+ public void enum_() {
+ assertThat(findOld().myEnum).isEqualTo(MyEnum.VALUE2);
+ }
+
+ private static MyEntity findOld() {
+ return find(1L);
+ }
+
+ private static MyEntity find(long id) {
+ return given().when().get("/compatibility/{id}", id).then()
+ .assertThat().statusCode(is(Status.OK.getStatusCode()))
+ .extract().as(MyEntity.class);
+ }
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestInGraalITCase.java b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestInGraalITCase.java
new file mode 100644
index 0000000000000..4564e10519e9a
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/test/java/io/quarkus/it/hibernate/compatibility/CompatibilityTestInGraalITCase.java
@@ -0,0 +1,8 @@
+package io.quarkus.it.hibernate.compatibility;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+public class CompatibilityTestInGraalITCase extends CompatibilityTest {
+
+}
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/test/resources/db/migration/V1.0.0__orm5-6.sql b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/test/resources/db/migration/V1.0.0__orm5-6.sql
new file mode 100644
index 0000000000000..a84c82b0c182a
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/src/test/resources/db/migration/V1.0.0__orm5-6.sql
@@ -0,0 +1,184 @@
+--
+-- PostgreSQL database dump
+--
+
+-- Dumped from database version 14.7 (Debian 14.7-1.pgdg110+1)
+-- Dumped by pg_dump version 14.7 (Debian 14.7-1.pgdg110+1)
+
+SET statement_timeout = 0;
+SET lock_timeout = 0;
+SET idle_in_transaction_session_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = on;
+SELECT pg_catalog.set_config('search_path', '', false);
+SET check_function_bodies = false;
+SET xmloption = content;
+SET client_min_messages = warning;
+SET row_security = off;
+
+--
+-- Name: gengendefallocsize; Type: SEQUENCE; Schema: public; Owner: hibernate_orm_test
+--
+
+CREATE SEQUENCE public.gengendefallocsize
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE public.gengendefallocsize OWNER TO hibernate_orm_test;
+
+--
+-- Name: hibernate_sequence; Type: SEQUENCE; Schema: public; Owner: hibernate_orm_test
+--
+
+CREATE SEQUENCE public.hibernate_sequence
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE public.hibernate_sequence OWNER TO hibernate_orm_test;
+
+SET default_tablespace = '';
+
+SET default_table_access_method = heap;
+
+--
+-- Name: myentity; Type: TABLE; Schema: public; Owner: hibernate_orm_test
+--
+
+CREATE TABLE public.myentity (
+ id bigint NOT NULL,
+ duration bigint,
+ instant timestamp without time zone,
+ intarray bytea,
+ myenum integer,
+ offsetdatetime timestamp without time zone,
+ stringlist bytea,
+ uuid uuid,
+ zoneddatetime timestamp without time zone
+);
+
+
+ALTER TABLE public.myentity OWNER TO hibernate_orm_test;
+
+--
+-- Name: myentity_gengendefallocsize; Type: TABLE; Schema: public; Owner: hibernate_orm_test
+--
+
+CREATE TABLE public.myentity_gengendefallocsize (
+ id bigint NOT NULL
+);
+
+
+ALTER TABLE public.myentity_gengendefallocsize OWNER TO hibernate_orm_test;
+
+--
+-- Name: myentity_seqgendefallocsize; Type: TABLE; Schema: public; Owner: hibernate_orm_test
+--
+
+CREATE TABLE public.myentity_seqgendefallocsize (
+ id bigint NOT NULL
+);
+
+
+ALTER TABLE public.myentity_seqgendefallocsize OWNER TO hibernate_orm_test;
+
+--
+-- Name: seqgendefallocsize; Type: SEQUENCE; Schema: public; Owner: hibernate_orm_test
+--
+
+CREATE SEQUENCE public.seqgendefallocsize
+ START WITH 1
+ INCREMENT BY 50
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE public.seqgendefallocsize OWNER TO hibernate_orm_test;
+
+--
+-- Data for Name: myentity; Type: TABLE DATA; Schema: public; Owner: hibernate_orm_test
+--
+
+COPY public.myentity (id, duration, instant, intarray, myenum, offsetdatetime, stringlist, uuid, zoneddatetime) FROM stdin;
+1 59000000000 2018-01-01 11:58:30 \\xaced0005757200025b494dba602676eab2a502000078700000000300000000000000010000002a 1 2018-01-01 11:58:30 \\xaced0005737200136a6176612e7574696c2e41727261794c6973747881d21d99c7619d03000149000473697a657870000000027704000000027400036f6e6574000374776f78 f49c6ba8-8d7f-417a-a255-d594dddf729f 2018-01-01 11:58:30
+2 \N \N \N \N \N \N \N \N
+\.
+
+
+--
+-- Data for Name: myentity_gengendefallocsize; Type: TABLE DATA; Schema: public; Owner: hibernate_orm_test
+--
+
+COPY public.myentity_gengendefallocsize (id) FROM stdin;
+1
+2
+\.
+
+
+--
+-- Data for Name: myentity_seqgendefallocsize; Type: TABLE DATA; Schema: public; Owner: hibernate_orm_test
+--
+
+COPY public.myentity_seqgendefallocsize (id) FROM stdin;
+1
+2
+\.
+
+
+--
+-- Name: gengendefallocsize; Type: SEQUENCE SET; Schema: public; Owner: hibernate_orm_test
+--
+
+SELECT pg_catalog.setval('public.gengendefallocsize', 2, true);
+
+
+--
+-- Name: hibernate_sequence; Type: SEQUENCE SET; Schema: public; Owner: hibernate_orm_test
+--
+
+SELECT pg_catalog.setval('public.hibernate_sequence', 2, true);
+
+
+--
+-- Name: seqgendefallocsize; Type: SEQUENCE SET; Schema: public; Owner: hibernate_orm_test
+--
+
+SELECT pg_catalog.setval('public.seqgendefallocsize', 51, true);
+
+
+--
+-- Name: myentity_gengendefallocsize myentity_gengendefallocsize_pkey; Type: CONSTRAINT; Schema: public; Owner: hibernate_orm_test
+--
+
+ALTER TABLE ONLY public.myentity_gengendefallocsize
+ ADD CONSTRAINT myentity_gengendefallocsize_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: myentity myentity_pkey; Type: CONSTRAINT; Schema: public; Owner: hibernate_orm_test
+--
+
+ALTER TABLE ONLY public.myentity
+ ADD CONSTRAINT myentity_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: myentity_seqgendefallocsize myentity_seqgendefallocsize_pkey; Type: CONSTRAINT; Schema: public; Owner: hibernate_orm_test
+--
+
+ALTER TABLE ONLY public.myentity_seqgendefallocsize
+ ADD CONSTRAINT myentity_seqgendefallocsize_pkey PRIMARY KEY (id);
+
+
+--
+-- PostgreSQL database dump complete
+--
+
diff --git a/integration-tests/hibernate-orm-compatibility-5.6/postgresql/update-dump.sh b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/update-dump.sh
new file mode 100755
index 0000000000000..36bcf6ab371fd
--- /dev/null
+++ b/integration-tests/hibernate-orm-compatibility-5.6/postgresql/update-dump.sh
@@ -0,0 +1,31 @@
+#!/bin/bash -e
+
+MVN=../../../mvnw
+DB_CONTAINER_NAME=postgresql-database-gen
+# These should be the same as the username/password used by docker-maven-plugin in ./pom.xml
+DB_NAME=hibernate_orm_test
+DB_PORT=5431
+DB_USER=hibernate_orm_test
+DB_PASSWORD=hibernate_orm_test
+DUMP_LOCATION=src/test/resources/db/migration/V1.0.0__orm5-6.sql
+
+# Start container
+$MVN docker:stop docker:build docker:start -Dstart-containers -Ddocker.containerNamePattern=$DB_CONTAINER_NAME
+trap '$MVN docker:stop -Dstart-containers -Ddocker.containerNamePattern=$DB_CONTAINER_NAME' EXIT
+
+# Generate database
+$MVN -f ../database-generator clean install -Ddb-kind=postgresql
+# We use features in Hibernate ORM that are timezone-sensitive, e.g. ZoneOffsetDateTime normalization
+TZ=Europe/Paris
+QUARKUS_DATASOURCE_JDBC_URL="jdbc:postgresql://localhost:$DB_PORT/$DB_NAME" \
+QUARKUS_DATASOURCE_USERNAME="$DB_USER" \
+QUARKUS_DATASOURCE_PASSWORD="$DB_PASSWORD" \
+ java -jar ../database-generator/target/quarkus-app/quarkus-run.jar
+
+# Update the dump
+# https://stackoverflow.com/a/32611542/6692043
+echo "Updating dump at '$DUMP_LOCATION'"
+
+docker exec "$DB_CONTAINER_NAME" \
+ sh -c 'PGPASSWORD="$POSTGRES_PASSWORD" pg_dump --username "$POSTGRES_USER" "$POSTGRES_DB"' \
+ > "$DUMP_LOCATION"
\ No newline at end of file
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index 6d41e219f8048..3995a75f2be86 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -180,6 +180,8 @@
jpa-mssql
jpa-mysql
jpa-oracle
+ hibernate-orm-compatibility-5.6/mariadb
+ hibernate-orm-compatibility-5.6/postgresql
hibernate-orm-panache
hibernate-orm-rest-data-panache
hibernate-orm-graphql-panache