diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java index aee487e96fa8..384306c33243 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java @@ -15,13 +15,18 @@ public interface DialectSpecificSettings { /** * Specifies whether this database is running on an Autonomous Database Cloud Service. + *

+ * Ignored if Hibernate is able to determine this by querying the Oracle server at startup. * * @settingDefault {@code false} */ String ORACLE_AUTONOMOUS_DATABASE = "hibernate.dialect.oracle.is_autonomous"; /** - * Specifies whether this database's {@code MAX_STRING_SIZE} is set to {@code EXTENDED}. + * Specifies whether {@code MAX_STRING_SIZE} is set to {@code EXTENDED} on Oracle. + *

+ * Ignored if Hibernate is able to determine the value of {@code MAX_STRING_SIZE} by + * querying the Oracle server at startup. * * @settingDefault {@code false} */ @@ -29,6 +34,8 @@ public interface DialectSpecificSettings { /** * Specifies whether this database is accessed using a database service protected by Application Continuity. + *

+ * Ignored if Hibernate is able to determine this by querying the Oracle server at startup. * * @settingDefault {@code false} * @@ -37,7 +44,10 @@ public interface DialectSpecificSettings { String ORACLE_APPLICATION_CONTINUITY = "hibernate.dialect.oracle.application_continuity"; /** - * Specifies whether this database's {@code ansinull} setting is enabled. + * Specifies whether the {@code ansinull} setting is enabled on Sybase. + *

+ * Ignored if Hibernate is able to determine the value of {@code ansinull} + * by querying the server {@code @@options} at startup. * * @settingDefault {@code false} */ @@ -45,6 +55,9 @@ public interface DialectSpecificSettings { /** * Specifies the maximum page size on Sybase. + *

+ * Ignored if Hibernate is able to determine the page size by querying the + * server {@code @@maxpagesize} at startup. * * @settingDefault {@value org.hibernate.dialect.SybaseASEDialect#MAX_PAGE_SIZE} */ @@ -53,6 +66,9 @@ public interface DialectSpecificSettings { /** * Specifies the bytes per character to use based on the database's configured * charset. + *

+ * Ignored if Hibernate is able to determine the character set by querying the + * server {@code @@character_set_database} at startup. * * @settingDefault {@code 4} */ @@ -60,6 +76,9 @@ public interface DialectSpecificSettings { /** * Specifies whether the {@code NO_BACKSLASH_ESCAPES} sql mode is enabled. + *

+ * Ignored if Hibernate is able to determine this by querying the server + * {@code @@sql_mode} at startup. * * @settingDefault {@code false} */ @@ -69,18 +88,28 @@ public interface DialectSpecificSettings { * Specifies a custom CockroachDB version string. The expected format of the string is * the one returned from the {@code version()} function, e.g.: * {@code "CockroachDB CCL v23.1.8 (x86_64-pc-linux-gnu, built 2023/08/04 18:11:44, go1.19.10)"} + *

+ * Ignored if Hibernate is able to obtain the version string by querying the server at startup. */ String COCKROACH_VERSION_STRING = "hibernate.dialect.cockroach.version_string"; /** - * Specifies the compatibility level of the SQL Server database as returned by {@code select compatibility_level from sys.databases}. - * The number has three digits, the first two digits are the major version, the last digit is the minor version. + * Specifies the compatibility level of the SQL Server database as returned by + * {@code select compatibility_level from sys.databases}. + *

+ * The number has three digits: the first two digits are the major version, + * the last digit is the minor version. + *

+ * Ignored if Hibernate is able to determine this by querying the {@code sys.databases} + * table at startup. */ String SQL_SERVER_COMPATIBILITY_LEVEL = "hibernate.dialect.sqlserver.compatibility_level"; /** - * Specifies the LOB prefetch size. LOBs larger than this value will be read into memory as the HANA JDBC driver closes - * the LOB when the result set is closed. + * Specifies the LOB prefetch size. LOBs larger than this value will be read into + * memory as the HANA JDBC driver closes the LOB when the result set is closed. + *

+ * Ignored if Hibernate is able to determine this by querying the server at startup. * * @settingDefault {@code 1024} */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index d037b39bb5a1..690f32dce88b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -20,7 +20,9 @@ import org.hibernate.PessimisticLockException; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; +import org.hibernate.cfg.FetchSettings; import org.hibernate.dialect.aggregate.AggregateSupport; import org.hibernate.dialect.aggregate.MySQLAggregateSupport; import org.hibernate.dialect.function.CommonFunctionFactory; @@ -45,7 +47,6 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; -import org.hibernate.internal.util.StringHelper; import org.hibernate.mapping.CheckConstraint; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; @@ -89,6 +90,7 @@ import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.internal.util.JdbcExceptionHelper.extractSqlState; import static org.hibernate.internal.util.StringHelper.isNotEmpty; +import static org.hibernate.internal.util.StringHelper.split; import static org.hibernate.type.SqlTypes.BIGINT; import static org.hibernate.type.SqlTypes.BINARY; import static org.hibernate.type.SqlTypes.BIT; @@ -217,7 +219,7 @@ protected static DatabaseVersion createVersion(DialectResolutionInfo info) { protected static DatabaseVersion createVersion(DialectResolutionInfo info, DatabaseVersion defaultVersion) { final String versionString = info.getDatabaseVersion(); if ( versionString != null ) { - final String[] components = StringHelper.split( ".-", versionString ); + final String[] components = split( ".-", versionString ); if ( components.length >= 3 ) { try { final int majorVersion = parseInt( components[0] ); @@ -241,26 +243,19 @@ protected DatabaseVersion getMinimumSupportedVersion() { @Override protected void initDefaultProperties() { super.initDefaultProperties(); - getDefaultProperties().setProperty( Environment.MAX_FETCH_DEPTH, "2" ); + getDefaultProperties().setProperty( FetchSettings.MAX_FETCH_DEPTH, "2" ); } private MySQLStorageEngine createStorageEngine() { - String storageEngine = Environment.getProperties().getProperty( Environment.STORAGE_ENGINE ); - if (storageEngine == null) { - storageEngine = System.getProperty( Environment.STORAGE_ENGINE ); - } - if (storageEngine == null) { - return getDefaultMySQLStorageEngine(); - } - else if( "innodb".equalsIgnoreCase( storageEngine ) ) { - return InnoDBStorageEngine.INSTANCE; - } - else if( "myisam".equalsIgnoreCase( storageEngine ) ) { - return MyISAMStorageEngine.INSTANCE; - } - else { - throw new UnsupportedOperationException( "The " + storageEngine + " storage engine is not supported" ); - } + final String storageEngine = Environment.getProperties().getProperty( AvailableSettings.STORAGE_ENGINE ); + return storageEngine == null + ? getDefaultMySQLStorageEngine() + : switch ( storageEngine ) { + case "innodb" -> InnoDBStorageEngine.INSTANCE; + case "myisam" -> MyISAMStorageEngine.INSTANCE; + default -> throw new UnsupportedOperationException( + "The '" + storageEngine + "' storage engine is not supported" ); + }; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index e94bc41125a7..e1afde5658dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -34,8 +34,6 @@ import org.hibernate.dialect.temptable.TemporaryTableKind; import org.hibernate.dialect.unique.CreateTableUniqueDelegate; import org.hibernate.dialect.unique.UniqueDelegate; -import org.hibernate.engine.config.spi.ConfigurationService; -import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; @@ -86,7 +84,6 @@ import org.hibernate.type.NullType; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType; -import org.hibernate.type.descriptor.jdbc.BlobJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.NullJdbcType; import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType; @@ -108,8 +105,6 @@ import static org.hibernate.LockOptions.SKIP_LOCKED; import static org.hibernate.LockOptions.WAIT_FOREVER; import static org.hibernate.cfg.AvailableSettings.BATCH_VERSIONED_DATA; -import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE; -import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE; import static org.hibernate.dialect.OracleJdbcHelper.getArrayJdbcTypeConstructor; import static org.hibernate.dialect.OracleJdbcHelper.getNestedTableJdbcTypeConstructor; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; @@ -168,12 +163,11 @@ public class OracleDialect extends Dialect { private static final Pattern SQL_STATEMENT_TYPE_PATTERN = Pattern.compile( "^(?:/\\*.*?\\*/)?\\s*(select|insert|update|delete)\\s+.*?", CASE_INSENSITIVE ); - private static final int PARAM_LIST_SIZE_LIMIT_1000 = 1000; - - /** Starting from 23c, 65535 parameters are supported for the IN condition. */ + /** + * Starting from 23c, 65535 parameters are supported for the {@code IN} condition. + */ private static final int PARAM_LIST_SIZE_LIMIT_65535 = 65535; - - public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw"; + private static final int PARAM_LIST_SIZE_LIMIT_1000 = 1000; private static final String yqmSelect = "(trunc(%2$s, 'MONTH') + numtoyminterval(%1$s, 'MONTH') + (least(extract(day from %2$s), extract(day from last_day(trunc(%2$s, 'MONTH') + numtoyminterval(%1$s, 'MONTH')))) - 1))"; @@ -191,11 +185,10 @@ public class OracleDialect extends Dialect { @Override protected void applyAggregateColumnCheck(StringBuilder buf, AggregateColumn aggregateColumn) { final JdbcType jdbcType = aggregateColumn.getType().getJdbcType(); - if ( dialect.getVersion().isBefore( 23, 6 ) && jdbcType.isXml() ) { - // ORA-00600 when selecting XML columns that have a check constraint was fixed in 23.6 - return; + // ORA-00600 when selecting XML columns that have a check constraint was fixed in 23.6 + if ( !dialect.getVersion().isBefore( 23, 6 ) || !jdbcType.isXml() ) { + super.applyAggregateColumnCheck( buf, aggregateColumn ); } - super.applyAggregateColumnCheck( buf, aggregateColumn ); } }; @@ -239,40 +232,6 @@ public OracleDialect(DialectResolutionInfo info, OracleServerConfiguration serve this.driverMajorVersion = serverConfiguration.getDriverMajorVersion(); } - @Deprecated( since = "6.4" ) - protected static boolean isExtended(DialectResolutionInfo info) { - final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata(); - if ( databaseMetaData != null ) { - try ( java.sql.Statement statement = databaseMetaData.getConnection().createStatement() ) { - statement.execute( "select cast('string' as varchar2(32000)) from dual" ); - // succeeded, so MAX_STRING_SIZE == EXTENDED - return true; - } - catch ( SQLException ex ) { - // failed, so MAX_STRING_SIZE == STANDARD - // Ignore - } - } - // default to the dialect-specific configuration setting - return ConfigurationHelper.getBoolean( ORACLE_EXTENDED_STRING_SIZE, info.getConfigurationValues(), false ); - } - - @Deprecated( since = "6.4" ) - protected static boolean isAutonomous(DialectResolutionInfo info) { - final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata(); - if ( databaseMetaData != null ) { - try ( java.sql.Statement statement = databaseMetaData.getConnection().createStatement() ) { - return statement.executeQuery( "select 1 from dual where sys_context('USERENV','CLOUD_SERVICE') in ('OLTP','DWCS','JSON')" ) - .next(); - } - catch ( SQLException ex ) { - // Ignore - } - } - // default to the dialect-specific configuration setting - return ConfigurationHelper.getBoolean( ORACLE_AUTONOMOUS_DATABASE, info.getConfigurationValues(), false ); - } - public boolean isAutonomous() { return autonomous; } @@ -360,12 +319,12 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionFactory.coalesce(); functionContributions.getFunctionRegistry() - .patternDescriptorBuilder( "bitor", "(?1+?2-bitand(?1,?2))") + .patternDescriptorBuilder( "bitor", "(?1+?2-bitand(?1,?2))" ) .setExactArgumentCount( 2 ) .setArgumentTypeResolver( StandardFunctionArgumentTypeResolvers.ARGUMENT_OR_IMPLIED_RESULT_TYPE ) .register(); functionContributions.getFunctionRegistry() - .patternDescriptorBuilder( "bitxor", "(?1+?2-2*bitand(?1,?2))") + .patternDescriptorBuilder( "bitxor", "(?1+?2-2*bitand(?1,?2))" ) .setExactArgumentCount( 2 ) .setArgumentTypeResolver( StandardFunctionArgumentTypeResolvers.ARGUMENT_OR_IMPLIED_RESULT_TYPE ) .register(); @@ -1019,12 +978,6 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry typeContributions.contributeJdbcType( OracleReflectionStructJdbcType.INSTANCE ); } - // account for Oracle's deprecated support for LONGVARBINARY - // prefer BLOB, unless the user explicitly opts out - final boolean preferLong = serviceRegistry.requireService( ConfigurationService.class ) - .getSetting( PREFER_LONG_RAW, StandardConverters.BOOLEAN, false ); - typeContributions.contributeJdbcType( preferLong ? BlobJdbcType.PRIMITIVE_ARRAY_BINDING : BlobJdbcType.DEFAULT ); - if ( getVersion().isSameOrAfter( 21 ) ) { typeContributions.contributeJdbcType( OracleJsonJdbcType.INSTANCE ); typeContributions.contributeJdbcTypeConstructor( OracleJsonArrayJdbcTypeConstructor.NATIVE_INSTANCE ); @@ -1065,10 +1018,10 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry ) ); - if(getVersion().isSameOrAfter(23)) { + if ( getVersion().isSameOrAfter(23) ) { final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry(); - jdbcTypeRegistry.addDescriptor(OracleEnumJdbcType.INSTANCE); - jdbcTypeRegistry.addDescriptor(OracleOrdinalEnumJdbcType.INSTANCE); + jdbcTypeRegistry.addDescriptor( OracleEnumJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( OracleOrdinalEnumJdbcType.INSTANCE ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java index 32a188a9e5b5..7b73e06ec416 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java @@ -4,21 +4,22 @@ */ package org.hibernate.dialect; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DatabaseMetaData; +import java.sql.Driver; +import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.List; +import java.util.Map; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; -import org.hibernate.internal.util.config.ConfigurationHelper; import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_APPLICATION_CONTINUITY; import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE; import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE; +import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean; /** * Utility class that extract some initial configuration from the database for {@link OracleDialect}. @@ -62,7 +63,7 @@ public OracleServerConfiguration( boolean extended, int driverMajorVersion, int driverMinorVersion) { - this(autonomous, extended, false, driverMajorVersion, driverMinorVersion); + this( autonomous, extended, false, driverMajorVersion, driverMinorVersion ); } public OracleServerConfiguration( @@ -79,103 +80,25 @@ public OracleServerConfiguration( } public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolutionInfo info) { - Boolean extended = null; - Boolean autonomous = null; - Boolean applicationContinuity = null; - Integer majorVersion = null; - Integer minorVersion = null; - final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata(); - if ( databaseMetaData != null ) { - majorVersion = databaseMetaData.getDriverMajorVersion(); - minorVersion = databaseMetaData.getDriverMinorVersion(); - - try { - final Connection c = databaseMetaData.getConnection(); - - try (final Statement statement = c.createStatement()) { - - // Use Oracle JDBC replay statistics information to determine if this - // connection is protected by Application Continuity - try { - final Class statisticReportTypeEnum = Class.forName("oracle.jdbc.replay.ReplayableConnection$StatisticsReportType",false, Thread.currentThread().getContextClassLoader()); - final Field forCurrentConnection = statisticReportTypeEnum.getField("FOR_CURRENT_CONNECTION"); - - final Method getReplayStatistics = c.getClass().getMethod("getReplayStatistics", statisticReportTypeEnum); - - Object stats = getReplayStatistics.invoke(c,forCurrentConnection.get(null)); - - final Method getTotalRequests = stats.getClass().getMethod("getTotalRequests"); - final Method getTotalProtectedCalls = stats.getClass().getMethod("getTotalProtectedCalls"); - - final Long totalRequestsBefore = (Long)getTotalRequests.invoke(stats); - final Long protectedCallsBefore = (Long)getTotalProtectedCalls.invoke(stats); - - try (ResultSet r = statement.executeQuery("select 1")) { - r.next(); - } - - stats = getReplayStatistics.invoke(c,forCurrentConnection.get(null)); - - final Long totalRequestsAfter = (Long)getTotalRequests.invoke(stats); - final Long protectedCallsAfter = (Long)getTotalProtectedCalls.invoke(stats); - - // Application continuity is enabled on this database service if the number of - // total requests and the number of protected calls for this connection have - // both increased. - applicationContinuity = totalRequestsAfter > totalRequestsBefore && protectedCallsAfter > protectedCallsBefore; - } - catch(Exception e) { - // A ClassCastException or a NullPointerException are expected here in the case - // the Connection Factory is not the right one (not Replayable: ClassCastException) - // or if the database service has not been configured (server side) to enable - // application continuity (NullPointerException). - applicationContinuity = false; - } - - // continue the checks... - final ResultSet rs = statement.executeQuery( - "select cast('string' as varchar2(32000)), " + - "sys_context('USERENV','CLOUD_SERVICE') from dual" - ); - if (rs.next()) { - // succeeded, so MAX_STRING_SIZE == EXTENDED - extended = true; - autonomous = isAutonomous(rs.getString(2)); - } - } - } - catch (SQLException ex) { - // failed, so MAX_STRING_SIZE == STANDARD, still need to check autonomous - extended = false; - autonomous = isAutonomous( databaseMetaData ); - } - } // default to the dialect-specific configuration settings - if ( extended == null ) { - extended = ConfigurationHelper.getBoolean( - ORACLE_EXTENDED_STRING_SIZE, - info.getConfigurationValues(), - false - ); - } - if ( autonomous == null ) { - autonomous = ConfigurationHelper.getBoolean( - ORACLE_AUTONOMOUS_DATABASE, - info.getConfigurationValues(), - false - ); - } - if ( applicationContinuity == null ) { - applicationContinuity = ConfigurationHelper.getBoolean( - ORACLE_APPLICATION_CONTINUITY, - info.getConfigurationValues(), - false - ); - } - if ( majorVersion == null ) { + final Map configuration = info.getConfigurationValues(); + final boolean defaultExtended = getBoolean( ORACLE_EXTENDED_STRING_SIZE, configuration, false ); + final boolean defaultAutonomous = getBoolean( ORACLE_AUTONOMOUS_DATABASE, configuration, false ); + final boolean defaultContinuity = getBoolean( ORACLE_APPLICATION_CONTINUITY, configuration, false ); + + boolean extended; + boolean autonomous; + boolean applicationContinuity; + int majorVersion; + int minorVersion; + final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata(); + if ( databaseMetaData == null ) { + extended = defaultExtended; + autonomous = defaultAutonomous; + applicationContinuity = defaultContinuity; try { - java.sql.Driver driver = java.sql.DriverManager.getDriver( "jdbc:oracle:thin:" ); + final Driver driver = DriverManager.getDriver( "jdbc:oracle:thin:" ); majorVersion = driver.getMajorVersion(); minorVersion = driver.getMinorVersion(); } @@ -183,25 +106,98 @@ public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolut majorVersion = 19; minorVersion = 0; } - } + else { + majorVersion = databaseMetaData.getDriverMajorVersion(); + minorVersion = databaseMetaData.getDriverMinorVersion(); + try { + final Connection connection = databaseMetaData.getConnection(); // we should not close this + try ( final Statement statement = connection.createStatement() ) { + applicationContinuity = determineApplicationContinuity( connection, statement ); + autonomous = isAutonomous( statement ); + extended = isExtended( statement ); + } + } + catch (SQLException sqle) { + extended = defaultExtended; + autonomous = defaultAutonomous; + applicationContinuity = defaultContinuity; + } + } + return new OracleServerConfiguration( autonomous, extended, applicationContinuity, majorVersion, minorVersion ); } - private static boolean isAutonomous(String cloudServiceParam) { - return cloudServiceParam != null && List.of( "OLTP", "DWCS", "JDCS" ).contains( cloudServiceParam ); + private static boolean isExtended(Statement statement) { + try ( final ResultSet resultSet = + statement.executeQuery( "select cast('string' as varchar2(32000)) from dual" ) ) { + resultSet.next(); + // succeeded, so MAX_STRING_SIZE == EXTENDED + return true; + } + catch (SQLException ex) { + // failed, so MAX_STRING_SIZE == STANDARD, still need to check autonomous + return false; + } } - private static boolean isAutonomous(DatabaseMetaData databaseMetaData) { - try (final Statement statement = databaseMetaData.getConnection().createStatement()) { - return statement.executeQuery( - "select 1 from dual where sys_context('USERENV','CLOUD_SERVICE') in ('OLTP','DWCS','JDCS')" ) - .next(); + private static Boolean determineApplicationContinuity(Connection connection, Statement statement) { + // Use Oracle JDBC replay statistics information to determine if this + // connection is protected by Application Continuity + try { + final Class statisticReportTypeEnum = + Class.forName( "oracle.jdbc.replay.ReplayableConnection$StatisticsReportType", + false, Thread.currentThread().getContextClassLoader() ); + final Object forCurrentConnection = + statisticReportTypeEnum.getField( "FOR_CURRENT_CONNECTION" ).get( null ); + final Method getReplayStatistics = + connection.getClass().getMethod( "getReplayStatistics", statisticReportTypeEnum ); + final Class replayStatistics = getReplayStatistics.getReturnType(); + final Method getTotalRequests = replayStatistics.getMethod("getTotalRequests"); + final Method getTotalProtectedCalls = replayStatistics.getMethod("getTotalProtectedCalls"); + + final Object before = getReplayStatistics.invoke( connection, forCurrentConnection ); + final Long totalRequestsBefore = (Long) getTotalRequests.invoke( before ); + final Long protectedCallsBefore = (Long) getTotalProtectedCalls.invoke( before ); + + try ( final ResultSet resultSet = statement.executeQuery("select 1") ) { + resultSet.next(); + } + + final Object after = getReplayStatistics.invoke( connection, forCurrentConnection ); + final Long totalRequestsAfter = (Long) getTotalRequests.invoke( after ); + final Long protectedCallsAfter = (Long) getTotalProtectedCalls.invoke( after ); + + // Application continuity is enabled on this database service if the number of + // total requests and the number of protected calls for this connection have + // both increased. + return totalRequestsAfter > totalRequestsBefore + && protectedCallsAfter > protectedCallsBefore; + } + catch (Exception e) { + // A ClassCastException or a NullPointerException are expected here in the case + // the Connection Factory is not the right one (not Replayable: ClassCastException) + // or if the database service has not been configured (server side) to enable + // application continuity (NullPointerException). + return false; + } + } + + private static boolean isAutonomous(Statement statement) { + try ( final ResultSet resultSet = + statement.executeQuery( "select sys_context('USERENV','CLOUD_SERVICE') from dual" ) ) { + return resultSet.next() + && isAutonomous( resultSet.getString(1) ); } catch (SQLException ex) { - // Ignore + return false; } - return false; } + private static boolean isAutonomous(String type) { + return type != null && switch ( type ) { + case "OLTP", "DWCS", "JDCS" -> true; + default -> false; + }; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java index 37073c932a72..2e13cd603e53 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java @@ -204,7 +204,7 @@ public long getDefaultLobLength() { private static boolean isAnsiNull(DialectResolutionInfo info) { final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata(); if ( databaseMetaData != null ) { - try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) { + try ( java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) { final ResultSet rs = s.executeQuery( "SELECT @@options" ); if ( rs.next() ) { final byte[] optionBytes = rs.getBytes( 1 ); @@ -223,7 +223,7 @@ private static boolean isAnsiNull(DialectResolutionInfo info) { private int pageSize(DialectResolutionInfo info) { final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata(); if ( databaseMetaData != null ) { - try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) { + try ( java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) { final ResultSet rs = s.executeQuery( "SELECT @@maxpagesize" ); if ( rs.next() ) { return rs.getInt( 1 ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/OracleLongLobTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/OracleLongLobTypeTest.java deleted file mode 100644 index 498fce9435f3..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/OracleLongLobTypeTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.type; - -import java.sql.Blob; -import java.sql.Clob; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Lob; - -import org.hibernate.boot.MetadataSources; -import org.hibernate.boot.registry.StandardServiceRegistry; -import org.hibernate.boot.registry.StandardServiceRegistryBuilder; -import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.cfg.AvailableSettings; -import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.OracleDialect; -import org.hibernate.mapping.PersistentClass; -import org.hibernate.type.BasicType; -import org.hibernate.type.BasicTypeReference; -import org.hibernate.type.StandardBasicTypes; -import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; - -import org.hibernate.testing.orm.junit.JiraKey; -import org.hibernate.testing.junit4.BaseUnitTestCase; -import org.hibernate.testing.util.ServiceRegistryUtil; - -import org.junit.Test; - -import static org.junit.Assert.assertSame; - -/** - * A test asserting LONG/LONGRAW versus CLOB/BLOB resolution for various Oracle Dialects - * - * @author Steve Ebersole - */ -public class OracleLongLobTypeTest extends BaseUnitTestCase { - - @Test - @JiraKey( value = "HHH-10345" ) - public void testOracle12() { - check( OracleDialect.class, Primitives.class, StandardBasicTypes.BINARY, StandardBasicTypes.CHAR_ARRAY ); - check( OracleDialect.class, LobPrimitives.class, StandardBasicTypes.MATERIALIZED_BLOB, StandardBasicTypes.MATERIALIZED_CLOB_CHAR_ARRAY ); - check( OracleDialect.class, LobLocators.class, StandardBasicTypes.BLOB, StandardBasicTypes.CLOB ); - } - - @Test - @JiraKey( value = "HHH-10345" ) - public void testOracle12PreferLongRaw() { - check( OracleDialect.class, Primitives.class, StandardBasicTypes.BINARY, StandardBasicTypes.CHAR_ARRAY, true ); - check( OracleDialect.class, LobPrimitives.class, StandardBasicTypes.MATERIALIZED_BLOB, StandardBasicTypes.MATERIALIZED_CLOB_CHAR_ARRAY, true ); - check( OracleDialect.class, LobLocators.class, StandardBasicTypes.BLOB, StandardBasicTypes.CLOB, true ); - } - - private void check( - Class dialectClass, - Class entityClass, - BasicTypeReference binaryTypeClass, - BasicTypeReference charTypeClass) { - check( dialectClass, entityClass, binaryTypeClass, charTypeClass, false ); - } - - private void check( - Class dialectClass, - Class entityClass, - BasicTypeReference binaryTypeClass, - BasicTypeReference charTypeClass, - boolean preferLongRaw) { - StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistryBuilder() - .applySetting( AvailableSettings.DIALECT, dialectClass.getName() ) - .applySetting( OracleDialect.PREFER_LONG_RAW, Boolean.toString( preferLongRaw ) ) - .applySetting( "hibernate.temp.use_jdbc_metadata_defaults", false ) - .build(); - - try { - final MetadataImplementor mappings = (MetadataImplementor) new MetadataSources( ssr ) - .addAnnotatedClass( entityClass ) - .buildMetadata(); - mappings.orderColumns( false ); - mappings.validate(); - - final PersistentClass entityBinding = mappings.getEntityBinding( entityClass.getName() ); - final JdbcTypeRegistry jdbcTypeRegistry = mappings.getTypeConfiguration() - .getJdbcTypeRegistry(); - - BasicType type; - - type = (BasicType) entityBinding.getProperty( "binaryData" ).getType(); - assertSame( jdbcTypeRegistry.getDescriptor( binaryTypeClass.getSqlTypeCode() ), type.getJdbcType() ); - type = (BasicType) entityBinding.getProperty( "characterData" ).getType(); - assertSame( jdbcTypeRegistry.getDescriptor( charTypeClass.getSqlTypeCode() ), type.getJdbcType() ); - } - finally { - StandardServiceRegistryBuilder.destroy( ssr ); - } - } - - @Entity - public static class Primitives { - @Id - public Integer id; - public byte[] binaryData; - public char[] characterData; - } - - @Entity - public static class LobPrimitives { - @Id - public Integer id; - @Lob - public byte[] binaryData; - @Lob - public char[] characterData; - } - - @Entity - public static class LobLocators { - @Id - public Integer id; - @Lob - public Blob binaryData; - @Lob - public Clob characterData; - } -}