diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index c30df9a285..5838943cc7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -344,7 +344,7 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun } protected Dialect(DialectResolutionInfo info) { - this.version = info.makeCopyOrDefault( getMinimumSupportedVersion() ); + this.version = determineDatabaseVersion( info ); checkVersion(); registerDefaultKeywords(); registerKeywords(info); @@ -363,6 +363,17 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun } } + /** + * Determine the database version, as precise as possible and using Dialect-specific techniques, + * from a {@link DialectResolutionInfo} object. + * @param info The dialect resolution info that would be passed by Hibernate ORM + * to the constructor of a Dialect of the same type. + * @return The corresponding database version. + */ + public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) { + return info.makeCopyOrDefault( getMinimumSupportedVersion() ); + } + /** * Set appropriate default values for configuration properties. *
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java
index dbda5ccab5..4e71a59999 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java
@@ -126,7 +126,7 @@ public class H2Dialect extends Dialect {
private final OptionalTableUpdateStrategy optionalTableUpdateStrategy;
public H2Dialect(DialectResolutionInfo info) {
- this( parseVersion( info ) );
+ this( staticDetermineDatabaseVersion( info ) );
registerKeywords( info );
}
@@ -151,7 +151,13 @@ public class H2Dialect extends Dialect {
this.optionalTableUpdateStrategy = H2Dialect::usingMerge;
}
- private static DatabaseVersion parseVersion(DialectResolutionInfo info) {
+ @Override
+ public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
+ return staticDetermineDatabaseVersion(info);
+ }
+
+ // Static version necessary to call from constructor
+ private static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
DatabaseVersion version = info.makeCopyOrDefault( MINIMUM_VERSION );
if ( info.getDatabaseVersion() != null ) {
version = DatabaseVersion.make( version.getMajor(), version.getMinor(), parseBuildId( info ) );
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java
index 0d21eb3fb9..f755dd5451 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java
@@ -202,6 +202,10 @@ public class HANADialect extends Dialect {
return MINIMUM_VERSION;
}
+ @Override
+ public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
+ return HANAServerConfiguration.staticDetermineDatabaseVersion( info );
+ }
// Use column or row tables by default
public static final String USE_DEFAULT_TABLE_TYPE_COLUMN = "hibernate.dialect.hana.use_default_table_type_column";
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANAServerConfiguration.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANAServerConfiguration.java
index ace21b79aa..8acf732190 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/HANAServerConfiguration.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANAServerConfiguration.java
@@ -75,10 +75,10 @@ public class HANAServerConfiguration {
MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE
);
}
- return new HANAServerConfiguration( createVersion( info ), maxLobPrefetchSize );
+ return new HANAServerConfiguration( staticDetermineDatabaseVersion( info ), maxLobPrefetchSize );
}
- private static DatabaseVersion createVersion(DialectResolutionInfo info) {
+ static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
// Parse the version according to https://answers.sap.com/questions/9760991/hana-sps-version-check.html
final String versionString = info.getDatabaseVersion();
int majorVersion = 1;
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java
index c98cf0806f..63962ddd88 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java
@@ -174,11 +174,17 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
}
public SQLServerDialect(DialectResolutionInfo info) {
- this( determineDatabaseVersion( info ) );
+ this( staticDetermineDatabaseVersion( info ) );
registerKeywords( info );
}
- private static DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
+ @Override
+ public DatabaseVersion determineDatabaseVersion(DialectResolutionInfo info) {
+ return staticDetermineDatabaseVersion(info);
+ }
+
+ // Static version necessary to call from constructor
+ private static DatabaseVersion staticDetermineDatabaseVersion(DialectResolutionInfo info) {
final Integer compatibilityLevel = getCompatibilityLevel( info );
if ( compatibilityLevel != null ) {
final int majorVersion = compatibilityLevel / 10;
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SimpleDatabaseVersion.java b/hibernate-core/src/main/java/org/hibernate/dialect/SimpleDatabaseVersion.java
index 7702037b02..6903fa0016 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/SimpleDatabaseVersion.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/SimpleDatabaseVersion.java
@@ -6,6 +6,8 @@
*/
package org.hibernate.dialect;
+import java.util.Objects;
+
/**
* Simple version of DatabaseVersion
*/
@@ -101,4 +103,21 @@ public class SimpleDatabaseVersion implements DatabaseVersion {
}
return version.toString();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+ SimpleDatabaseVersion that = (SimpleDatabaseVersion) o;
+ return major == that.major && minor == that.minor && micro == that.micro;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash( major, minor, micro );
+ }
}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/metadata/MetadataAccessTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/metadata/MetadataAccessTests.java
index 02bf8327c6..1dea8f661a 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/metadata/MetadataAccessTests.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/metadata/MetadataAccessTests.java
@@ -12,9 +12,11 @@ import static org.junit.jupiter.api.Assertions.fail;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
+import java.util.Map;
import java.util.stream.Stream;
import org.hibernate.HibernateException;
+import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
@@ -33,12 +35,18 @@ import org.hibernate.dialect.PostgresPlusDialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.dialect.SpannerDialect;
import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl;
+import org.hibernate.engine.jdbc.dialect.spi.DialectFactory;
+import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
+import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfoSource;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.service.spi.ServiceException;
+import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.testing.env.TestingDatabaseInfo;
import org.hibernate.testing.logger.Triggerable;
+import org.hibernate.testing.orm.junit.DialectContext;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.logger.LoggerInspectionExtension;
import org.junit.jupiter.api.BeforeEach;
@@ -89,8 +97,7 @@ public class MetadataAccessTests {
.doesNotContainKeys( JdbcSettings.DIALECT, JdbcSettings.JAKARTA_HBM2DDL_DB_NAME );
try (StandardServiceRegistry registry = registryBuilder.build()) {
- final JdbcEnvironment jdbcEnvironment = registry.getService( JdbcEnvironment.class );
- final Dialect dialect = jdbcEnvironment.getDialect();
+ final Dialect dialect = getDialect( registry );
assertThat( dialect ).isNotNull();
assertThat( dialect ).isInstanceOf( H2Dialect.class );
}
@@ -134,7 +141,20 @@ public class MetadataAccessTests {
@ParameterizedTest
@MethodSource("dialects")
- void testAccessDisabledExplicitDialect(String productName, Class> dialectClass, DatabaseVersion expectedDatabaseVersion) {
+ void testAccessDisabledExplicitDialect(String productName, Class> dialectClass,
+ DatabaseVersion expectedDatabaseVersion) {
+ try ( StandardServiceRegistry registry = createRegistryWithMetadataAccessDisabledAndDialect( dialectClass ) ) {
+ final Dialect dialect = getDialect( registry );
+ assertThat( dialect ).isInstanceOf( dialectClass );
+ assertThat( dialect.getVersion() ).isEqualTo( expectedDatabaseVersion );
+ }
+
+ assertThat( triggerable.triggerMessages() )
+ .as( triggerable.toString() )
+ .isEmpty();
+ }
+
+ private StandardServiceRegistry createRegistryWithMetadataAccessDisabledAndDialect(Class> dialectClass) {
final StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
registryBuilder.clearSettings();
@@ -143,16 +163,7 @@ public class MetadataAccessTests {
assertThat( registryBuilder.getSettings() )
.doesNotContainKeys( JdbcSettings.JAKARTA_HBM2DDL_DB_NAME );
- try (StandardServiceRegistry registry = registryBuilder.build()) {
- final JdbcEnvironment jdbcEnvironment = registry.getService( JdbcEnvironment.class );
- final Dialect dialect = jdbcEnvironment.getDialect();
- assertThat( dialect ).isInstanceOf( dialectClass );
- assertThat( dialect.getVersion() ).isEqualTo( expectedDatabaseVersion );
- }
-
- assertThat( triggerable.triggerMessages() )
- .as( triggerable.toString() )
- .isEmpty();
+ return registryBuilder.build();
}
@ParameterizedTest
@@ -190,8 +201,7 @@ public class MetadataAccessTests {
registryBuilder.applySetting( JdbcSettings.ALLOW_METADATA_ON_BOOT, false );
try (StandardServiceRegistry registry = registryBuilder.build()) {
- final JdbcEnvironment jdbcEnvironment = registry.getService( JdbcEnvironment.class );
- final Dialect dialect = jdbcEnvironment.getDialect();
+ final Dialect dialect = getDialect( registry );
fail( "Should fail to boot - " + dialect );
}
catch (ServiceException expected) {
@@ -201,6 +211,50 @@ public class MetadataAccessTests {
}
}
+ @Test
+ void testDetermineDatabaseVersion() {
+ final Dialect metadataAccessDisabledDialect;
+ try ( StandardServiceRegistry registry =
+ createRegistryWithMetadataAccessDisabledAndDialect( DialectContext.getDialectClass() ) ) {
+ // The version on this dialect may be anything, but most likely will be the minimum version.
+ // We're not interested in that, but in how determineDatabaseVersion() behaves for this dialect,
+ // when passed actual resolution info -- which Quarkus may do.
+ metadataAccessDisabledDialect = getDialect( registry );
+ }
+
+ try ( StandardServiceRegistry registry = createRegistryWithTestedDatabaseAndMetadataAccessAllowed() ) {
+ final Dialect metadataAccessAllowedDialect = getDialect( registry );
+
+ // We expect determineDatabaseVersion(), when called on metadataAccessDisabledDialect,
+ // to return the version that would have been returned,
+ // had we booted up with auto-detection of version (metadata access allowed).
+ assertThat( metadataAccessDisabledDialect.determineDatabaseVersion( getDialectResolutionInfo( registry ) ) )
+ .isEqualTo( metadataAccessAllowedDialect.getVersion() );
+ }
+ }
+
+ private StandardServiceRegistry createRegistryWithTestedDatabaseAndMetadataAccessAllowed() {
+ final StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
+
+ registryBuilder.addInitiator( new CapturingDialectFactory.Initiator() );
+
+ // allow access to the jdbc metadata
+ registryBuilder.applySetting( JdbcSettings.ALLOW_METADATA_ON_BOOT, true );
+
+ // leave connection info as defined in global test configuration (most likely system properties)
+
+ return registryBuilder.build();
+ }
+
+ private static Dialect getDialect(StandardServiceRegistry registry) {
+ return registry.getService( JdbcEnvironment.class ).getDialect();
+ }
+
+ private static DialectResolutionInfo getDialectResolutionInfo(StandardServiceRegistry registry) {
+ return ( (CapturingDialectFactory) registry.getService( DialectFactory.class ) )
+ .capturedDialectResolutionInfoSource.getDialectResolutionInfo();
+ }
+
// Ugly hack because neither MINIMUM_VERSION nor getMinimumSupportedVersion()
// can be accessed from this test.
private static DatabaseVersion getVersionConstant(Class extends Dialect> dialectClass, String versionConstantName) {
@@ -213,4 +267,29 @@ public class MetadataAccessTests {
throw new RuntimeException( "Error extracting '" + versionConstantName + "' from '" + dialectClass + "'", e );
}
}
+
+ // A hack to easily retrieve DialectResolutionInfo exactly as it would be constructed by Hibernate ORM
+ private static class CapturingDialectFactory extends DialectFactoryImpl {
+ static class Initiator implements StandardServiceInitiator