From d79f1fd8d3328638e34c3bcea13fd0f59fba8ced Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 18 Jan 2025 10:38:21 +0100 Subject: [PATCH 1/6] refactor the logic in OracleServerConfiguration to make the flow here a bit clearer --- .../dialect/OracleServerConfiguration.java | 212 +++++++++--------- 1 file changed, 104 insertions(+), 108 deletions(-) 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..65a9d7e263b7 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; + }; + } } From ab08353fa40d808909c2ec55b65c753787d576ee Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 18 Jan 2025 10:40:05 +0100 Subject: [PATCH 2/6] remove two deprecated methods from OracleDialect --- .../org/hibernate/dialect/OracleDialect.java | 54 ++++--------------- 1 file changed, 9 insertions(+), 45 deletions(-) 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..7f4deab5d523 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -19,6 +19,7 @@ import org.hibernate.QueryTimeoutException; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; +import org.hibernate.cfg.DialectSpecificSettings; import org.hibernate.dialect.aggregate.AggregateSupport; import org.hibernate.dialect.aggregate.OracleAggregateSupport; import org.hibernate.dialect.function.CommonFunctionFactory; @@ -108,8 +109,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; @@ -191,11 +190,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 +237,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 +324,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(); @@ -1065,10 +1029,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 ); } } From 325810ff73e1de257e890e5c13ceb48cc7ff96e9 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 18 Jan 2025 10:42:06 +0100 Subject: [PATCH 3/6] move an Oracle config property to DialectSpecificSettings even if this is maybe not *precisely* the intent of DialectSpecificSettings --- .../java/org/hibernate/cfg/DialectSpecificSettings.java | 7 +++++++ .../src/main/java/org/hibernate/dialect/OracleDialect.java | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) 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..746c98c43260 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java @@ -38,6 +38,13 @@ public interface DialectSpecificSettings { /** * Specifies whether this database's {@code ansinull} setting is enabled. + * Enables the use of the deprecated type {@code LONGVARBINARY} on Oracle instead of {@code BLOB}. + * + * @settingDefault {@code false} + */ + String ORACLE_PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw"; + + /** * * @settingDefault {@code false} */ 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 7f4deab5d523..35dda7f004b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -172,7 +172,11 @@ public class OracleDialect extends Dialect { /** Starting from 23c, 65535 parameters are supported for the 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"; + /** + * @deprecated Use {@link DialectSpecificSettings#ORACLE_PREFER_LONG_RAW}. + */ + @Deprecated(since = "7.0", forRemoval = true) + public static final String PREFER_LONG_RAW = DialectSpecificSettings.ORACLE_PREFER_LONG_RAW; 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))"; From bb104e34bc3123c327919df63dc302af7707cb5b Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 18 Jan 2025 10:42:51 +0100 Subject: [PATCH 4/6] jdoc clarifications in DialectSpecificSettings make things explicit that were implicit --- .../cfg/DialectSpecificSettings.java | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) 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 746c98c43260..8e25dfe20b99 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,6 @@ public interface DialectSpecificSettings { String ORACLE_APPLICATION_CONTINUITY = "hibernate.dialect.oracle.application_continuity"; /** - * Specifies whether this database's {@code ansinull} setting is enabled. * Enables the use of the deprecated type {@code LONGVARBINARY} on Oracle instead of {@code BLOB}. * * @settingDefault {@code false} @@ -45,6 +51,10 @@ public interface DialectSpecificSettings { String ORACLE_PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw"; /** + * 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} */ @@ -52,6 +62,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} */ @@ -60,6 +73,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} */ @@ -67,6 +83,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} */ @@ -76,18 +95,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} */ From adf607a629db1247f50037f8eac1f9ce2b34d6e5 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 18 Jan 2025 10:43:55 +0100 Subject: [PATCH 5/6] minor code cleanups in MySQLDialect note: system properties are already included in Environment properties --- .../org/hibernate/dialect/MySQLDialect.java | 32 ++++++++----------- .../hibernate/dialect/SybaseASEDialect.java | 4 +-- 2 files changed, 16 insertions(+), 20 deletions(-) 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..6e8f88deb42f 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; @@ -89,6 +91,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 +220,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 +244,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/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 ); From 688c8b74a957ea04fa68ce6cbb871e615cf41175 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 18 Jan 2025 10:57:32 +0100 Subject: [PATCH 6/6] completely remove hibernate.dialect.oracle.prefer_long_raw Looks like this has not worked since probably H 6.0 The issue here is that the test for this feature was one of these tests written to look at the Hibernate metamodel and not at the actual DDL that was ultimately generated. This is why I despise "unit" testing instead of testing actual user-visible functionality. Anyway this was an undocumented feature so it can just be removed. --- .../cfg/DialectSpecificSettings.java | 7 - .../org/hibernate/dialect/MySQLDialect.java | 1 - .../org/hibernate/dialect/OracleDialect.java | 21 +-- .../dialect/OracleServerConfiguration.java | 4 +- .../orm/test/type/OracleLongLobTypeTest.java | 127 ------------------ 5 files changed, 5 insertions(+), 155 deletions(-) delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/type/OracleLongLobTypeTest.java 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 8e25dfe20b99..384306c33243 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java @@ -43,13 +43,6 @@ public interface DialectSpecificSettings { */ String ORACLE_APPLICATION_CONTINUITY = "hibernate.dialect.oracle.application_continuity"; - /** - * Enables the use of the deprecated type {@code LONGVARBINARY} on Oracle instead of {@code BLOB}. - * - * @settingDefault {@code false} - */ - String ORACLE_PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw"; - /** * Specifies whether the {@code ansinull} setting is enabled on Sybase. *

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 6e8f88deb42f..690f32dce88b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -47,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; 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 35dda7f004b8..e1afde5658dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -19,7 +19,6 @@ import org.hibernate.QueryTimeoutException; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; -import org.hibernate.cfg.DialectSpecificSettings; import org.hibernate.dialect.aggregate.AggregateSupport; import org.hibernate.dialect.aggregate.OracleAggregateSupport; import org.hibernate.dialect.function.CommonFunctionFactory; @@ -35,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; @@ -87,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; @@ -167,16 +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. */ - private static final int PARAM_LIST_SIZE_LIMIT_65535 = 65535; - /** - * @deprecated Use {@link DialectSpecificSettings#ORACLE_PREFER_LONG_RAW}. + * Starting from 23c, 65535 parameters are supported for the {@code IN} condition. */ - @Deprecated(since = "7.0", forRemoval = true) - public static final String PREFER_LONG_RAW = DialectSpecificSettings.ORACLE_PREFER_LONG_RAW; + private static final int PARAM_LIST_SIZE_LIMIT_65535 = 65535; + 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))"; @@ -987,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 ); 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 65a9d7e263b7..7b73e06ec416 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java @@ -130,7 +130,7 @@ public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolut private static boolean isExtended(Statement statement) { try ( final ResultSet resultSet = - statement.executeQuery( "select cast('string' as varchar2(32000)) from dual" ) ) { + statement.executeQuery( "select cast('string' as varchar2(32000)) from dual" ) ) { resultSet.next(); // succeeded, so MAX_STRING_SIZE == EXTENDED return true; @@ -185,7 +185,7 @@ private static Boolean determineApplicationContinuity(Connection connection, Sta private static boolean isAutonomous(Statement statement) { try ( final ResultSet resultSet = - statement.executeQuery( "select sys_context('USERENV','CLOUD_SERVICE') from dual" ) ) { + statement.executeQuery( "select sys_context('USERENV','CLOUD_SERVICE') from dual" ) ) { return resultSet.next() && isAutonomous( resultSet.getString(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; - } -}