diff --git a/documentation/src/main/asciidoc/introduction/Tuning.adoc b/documentation/src/main/asciidoc/introduction/Tuning.adoc index d5dede466acd..6849c0ffc3a3 100644 --- a/documentation/src/main/asciidoc/introduction/Tuning.adoc +++ b/documentation/src/main/asciidoc/introduction/Tuning.adoc @@ -122,7 +122,7 @@ The default fetch size can be overridden for a given query by calling link:{doc- [WARNING] ==== The Oracle JDBC driver defaults to a JDBC fetch size of 10. -You should _always_ set explicitly `hibernate.jdbc.fetch_size` if you're using Oracle, or, even better, specify the parameter `defaultRowPrefetch` in the JDBC connection URL. +You should _always_ set `hibernate.jdbc.fetch_size` explicitly if you're using Oracle, or, even better, specify the parameter `defaultRowPrefetch` in the JDBC connection URL. ==== [[statement-batching]] diff --git a/hibernate-agroal/src/main/java/org/hibernate/agroal/internal/AgroalConnectionProvider.java b/hibernate-agroal/src/main/java/org/hibernate/agroal/internal/AgroalConnectionProvider.java index 887b91c81b1d..498cfd1affaf 100644 --- a/hibernate-agroal/src/main/java/org/hibernate/agroal/internal/AgroalConnectionProvider.java +++ b/hibernate-agroal/src/main/java/org/hibernate/agroal/internal/AgroalConnectionProvider.java @@ -39,6 +39,9 @@ import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; import static org.hibernate.cfg.AgroalSettings.AGROAL_CONFIG_PREFIX; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.toIsolationNiceName; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getFetchSize; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getIsolation; import static org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.allowJdbcMetadataAccess; /** @@ -81,7 +84,7 @@ private static String extractIsolationAsString(Map properties) { final Integer isolation = ConnectionProviderInitiator.extractIsolation( properties ); return isolation != null ? // Agroal resolves transaction isolation from the 'nice' name - ConnectionProviderInitiator.toIsolationNiceName( isolation ) + toIsolationNiceName( isolation ) : null; } @@ -177,11 +180,12 @@ public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect) { : extractDriverNameFromMetadata(), dialect.getVersion(), Boolean.toString( acfc.autoCommit() ), - acfc.jdbcTransactionIsolation() != null - ? ConnectionProviderInitiator.toIsolationNiceName( acfc.jdbcTransactionIsolation().level() ) - : null, + acfc.jdbcTransactionIsolation() != null && acfc.jdbcTransactionIsolation().isDefined() + ? toIsolationNiceName( acfc.jdbcTransactionIsolation().level() ) + : toIsolationNiceName( getIsolation( agroalDataSource ) ), acpc.minSize(), - acpc.maxSize() + acpc.maxSize(), + getFetchSize( agroalDataSource ) ); } diff --git a/hibernate-c3p0/src/main/java/org/hibernate/c3p0/internal/C3P0ConnectionProvider.java b/hibernate-c3p0/src/main/java/org/hibernate/c3p0/internal/C3P0ConnectionProvider.java index 3677142246e2..a3eb44e2f418 100644 --- a/hibernate-c3p0/src/main/java/org/hibernate/c3p0/internal/C3P0ConnectionProvider.java +++ b/hibernate-c3p0/src/main/java/org/hibernate/c3p0/internal/C3P0ConnectionProvider.java @@ -9,7 +9,6 @@ import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.cfg.JdbcSettings; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator; import org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProviderConfigurationException; @@ -40,7 +39,12 @@ import static org.hibernate.cfg.C3p0Settings.C3P0_MAX_STATEMENTS; import static org.hibernate.cfg.C3p0Settings.C3P0_MIN_SIZE; import static org.hibernate.cfg.C3p0Settings.C3P0_TIMEOUT; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.extractIsolation; import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.extractSetting; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.getConnectionProperties; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.toIsolationNiceName; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getFetchSize; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getIsolation; import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean; import static org.hibernate.internal.util.config.ConfigurationHelper.getInteger; @@ -149,23 +153,28 @@ public void configure(Map properties) { // as soon as we obtain a new connection. This maybe isn't ideal, // and it's not what we do with Agroal or Hikari. autocommit = getBoolean( JdbcSettings.AUTOCOMMIT, properties ); // defaults to false - isolation = ConnectionProviderInitiator.extractIsolation( properties ); + isolation = extractIsolation( properties ); - final Properties connectionProps = ConnectionProviderInitiator.getConnectionProperties( properties ); - final Map poolSettings = poolSettings( properties ); + final Properties connectionProps = getConnectionProperties( properties ); + final var poolSettings = poolSettings( properties ); dataSource = createDataSource( jdbcUrl, connectionProps, poolSettings ); + final Integer fetchSize = getFetchSize( dataSource ); + if ( isolation == null ) { + isolation = getIsolation( dataSource ); + } dbInfoProducer = dialect -> new DatabaseConnectionInfoImpl( C3P0ConnectionProvider.class, jdbcUrl, jdbcDriverClass, dialect.getVersion(), Boolean.toString( autocommit ), - isolation == null ? null : ConnectionProviderInitiator.toIsolationNiceName( isolation ), + isolation == null ? null : toIsolationNiceName( isolation ), requireNonNullElse( getInteger( C3P0_STYLE_MIN_POOL_SIZE.substring( 5 ), poolSettings ), DEFAULT_MIN_POOL_SIZE ), requireNonNullElse( getInteger( C3P0_STYLE_MAX_POOL_SIZE.substring( 5 ), poolSettings ), - DEFAULT_MAX_POOL_SIZE ) + DEFAULT_MAX_POOL_SIZE ), + fetchSize ); } diff --git a/hibernate-c3p0/src/test/java/org/hibernate/test/c3p0/C3P0ConnectionProviderTest.java b/hibernate-c3p0/src/test/java/org/hibernate/test/c3p0/C3P0ConnectionProviderTest.java index 96a4d92a38f0..e852749220a0 100644 --- a/hibernate-c3p0/src/test/java/org/hibernate/test/c3p0/C3P0ConnectionProviderTest.java +++ b/hibernate-c3p0/src/test/java/org/hibernate/test/c3p0/C3P0ConnectionProviderTest.java @@ -5,19 +5,15 @@ package org.hibernate.test.c3p0; import java.lang.management.ManagementFactory; -import java.util.HashMap; -import java.util.Map; import java.util.Set; import javax.management.MBeanServer; import javax.management.ObjectName; import org.hibernate.c3p0.internal.C3P0ConnectionProvider; -import org.hibernate.cfg.Environment; import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.ConnectionProviderJdbcConnectionAccess; import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.orm.junit.SkipForDialect; import org.junit.Test; @@ -92,17 +88,4 @@ public void testHHH6635() throws Exception { assertTrue( "PooledDataSource BMean not found, please verify version of c3p0", mbeanfound ); } - - @Test @JiraKey(value="HHH-9498") - public void testIsolationPropertyCouldBeEmpty() { - C3P0ConnectionProvider provider = new C3P0ConnectionProvider(); - try { - Map configuration = new HashMap<>(); - configuration.put( Environment.ISOLATION, "" ); - provider.configure( configuration ); - } - finally { - provider.stop(); - } - } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/JdbcLogging.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/JdbcLogging.java index 6b10d9976b52..ebeb00da3c3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/JdbcLogging.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/JdbcLogging.java @@ -152,8 +152,32 @@ public interface JdbcLogging extends BasicLogger { @Message(value = "Unable to roll back isolated connection on exception ", id = 100021) void unableToRollBackIsolatedConnection(@Cause Exception ignored); + @LogMessage(level = DEBUG) + @Message(value = "Using default JDBC fetch size: %s", id = 100022) + void usingFetchSize(int fetchSize); + + @LogMessage(level = WARN) + @Message(value = "Low default JDBC fetch size: %s (consider setting 'hibernate.jdbc.fetch_size')", id = 100023) + void warnLowFetchSize(int fetchSize); + + @LogMessage(level = TRACE) + @Message(value = "JDBC fetch size: %s", id = 100024) + void fetchSize(int fetchSize); + + @LogMessage(level = DEBUG) + @Message(value = "Low JDBC fetch size: %s (consider setting 'hibernate.jdbc.fetch_size')", id = 100025) + void lowFetchSize(int fetchSize); + + @LogMessage(level = TRACE) + @Message(value = "Setting JDBC fetch size: %s", id = 100026) + void settingFetchSize(int fetchSize); + + @LogMessage(level = TRACE) + @Message(value = "Setting JDBC query timeout: %s", id = 100027) + void settingQueryTimeout(int timeout); + @LogMessage(level = WARN) - @Message(value = "Called joinTransaction() on a non-JTA EntityManager (ignoring)", id = 100025) + @Message(value = "Called joinTransaction() on a non-JTA EntityManager (ignoring)", id = 100030) void callingJoinTransactionOnNonJtaEntityManager(); @LogMessage(level = TRACE) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatabaseConnectionInfoImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatabaseConnectionInfoImpl.java index 46c98c6c9d54..dc4f2addbc17 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatabaseConnectionInfoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatabaseConnectionInfoImpl.java @@ -4,8 +4,10 @@ */ package org.hibernate.engine.jdbc.connections.internal; +import java.sql.SQLException; import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.cfg.JdbcSettings; import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; @@ -14,6 +16,8 @@ import org.hibernate.internal.util.NullnessHelper; import org.hibernate.internal.util.config.ConfigurationHelper; +import javax.sql.DataSource; + import static org.hibernate.dialect.SimpleDatabaseVersion.ZERO_VERSION; import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.interpretIsolation; import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.toIsolationNiceName; @@ -37,6 +41,7 @@ public class DatabaseConnectionInfoImpl implements DatabaseConnectionInfo { protected final String isolationLevel; protected final Integer poolMinSize; protected final Integer poolMaxSize; + private final Integer fetchSize; public DatabaseConnectionInfoImpl( Class connectionProviderClass, @@ -46,7 +51,8 @@ public DatabaseConnectionInfoImpl( String autoCommitMode, String isolationLevel, Integer poolMinSize, - Integer poolMaxSize) { + Integer poolMaxSize, + Integer fetchSize) { this.connectionProviderClass = connectionProviderClass; this.jdbcUrl = nullIfEmpty( jdbcUrl ); this.jdbcDriver = nullIfEmpty( jdbcDriver ); @@ -55,6 +61,7 @@ public DatabaseConnectionInfoImpl( this.isolationLevel = nullIfEmpty( isolationLevel ); this.poolMinSize = poolMinSize; this.poolMaxSize = poolMaxSize; + this.fetchSize = fetchSize; } public DatabaseConnectionInfoImpl(Map settings, Dialect dialect) { @@ -67,12 +74,33 @@ public DatabaseConnectionInfoImpl(Map settings, Dialect dialect) determineIsolationLevel( settings ), // No setting for min. pool size null, - determinePoolMaxSize( settings ) + determinePoolMaxSize( settings ), + null ); } public DatabaseConnectionInfoImpl(Dialect dialect) { - this( null, null, null, dialect.getVersion(), null, null, null, null ); + this( null, null, null, dialect.getVersion(), null, null, null, null, null ); + } + + public static Integer getFetchSize(DataSource dataSource) { + try ( var conn = dataSource.getConnection() ) { + try ( var statement = conn.createStatement() ) { + return statement.getFetchSize(); + } + } + catch ( SQLException ignored ) { + return null; + } + } + + public static Integer getIsolation(DataSource dataSource) { + try ( var conn = dataSource.getConnection() ) { + return conn.getTransactionIsolation(); + } + catch ( SQLException ignored ) { + return null; + } } @Override @@ -110,16 +138,34 @@ public Integer getPoolMaxSize() { return poolMaxSize; } + @Override + public @Nullable Integer getJdbcFetchSize() { + return fetchSize; + } + @Override public String toInfoString() { - return "\tDatabase JDBC URL [" + handleEmpty( jdbcUrl ) + ']' + - "\n\tDatabase driver: " + handleEmpty( jdbcDriver ) + - "\n\tDatabase version: " + handleEmpty( dialectVersion ) + - "\n\tAutocommit mode: " + handleEmpty( autoCommitMode ) + - "\n\tIsolation level: " + handleEmpty( isolationLevel ) + - "\n\tPool: " + handleEmpty( connectionProviderClass ) + - "\n\tMinimum pool size: " + handleEmpty( poolMinSize ) + - "\n\tMaximum pool size: " + handleEmpty( poolMaxSize ); + return """ + \tDatabase JDBC URL [%s] + \tDatabase driver: %s + \tDatabase version: %s + \tAutocommit mode: %s + \tIsolation level: %s + \tJDBC fetch size: %s + \tPool: %s + \tMinimum pool size: %s + \tMaximum pool size: %s""" + .formatted( + handleEmpty( jdbcUrl ), + handleEmpty( jdbcDriver ), + handleEmpty( dialectVersion ), + handleEmpty( autoCommitMode ), + handleEmpty( isolationLevel ), + handleEmpty( fetchSize ), + handleEmpty( connectionProviderClass ), + handleEmpty( poolMinSize ), + handleEmpty( poolMaxSize ) + ); } private static String handleEmpty(String value) { @@ -131,6 +177,10 @@ private static String handleEmpty(DatabaseVersion dialectVersion) { } private static String handleEmpty(Integer value) { + return value != null ? ( value == 0 ? "none" : value.toString() ) : DEFAULT; + } + + private static String handleFetchSize(Integer value) { return value != null ? value.toString() : DEFAULT; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatasourceConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatasourceConnectionProviderImpl.java index d5cb499e5a8a..f85299f20b37 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatasourceConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatasourceConnectionProviderImpl.java @@ -160,16 +160,21 @@ public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect, Extract final String url; final String driver; final String isolationLevel; + final Integer fetchSize; if ( metaData != null ) { url = metaData.getUrl(); driver = metaData.getDriver(); - isolationLevel = toIsolationNiceName( metaData.getTransactionIsolation() ) + isolationLevel = + toIsolationNiceName( metaData.getTransactionIsolation() ) + " [default " + toIsolationNiceName( metaData.getDefaultTransactionIsolation() ) + "]"; + final int defaultFetchSize = metaData.getDefaultFetchSize(); + fetchSize = defaultFetchSize == -1 ? null : defaultFetchSize; } else { url = null; driver = null; isolationLevel = null; + fetchSize = null; } return new DatabaseConnectionInfoImpl( @@ -180,7 +185,8 @@ public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect, Extract null, isolationLevel, null, - null + null, + fetchSize ) { @Override public String toInfoString() { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java index 2512a1290c54..49c25cf32ca6 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java @@ -37,6 +37,8 @@ import org.hibernate.internal.log.ConnectionInfoLogger; import static org.hibernate.cfg.JdbcSettings.JAKARTA_JDBC_URL; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.extractIsolation; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.getConnectionProperties; import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.toIsolationNiceName; import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean; import static org.hibernate.internal.util.config.ConfigurationHelper.getInt; @@ -134,10 +136,10 @@ private static ConnectionCreator buildCreator( final String driverList = success ? driverClassName : driverList(); - final Properties connectionProps = ConnectionProviderInitiator.getConnectionProperties( configurationValues ); + final Properties connectionProps = getConnectionProperties( configurationValues ); final boolean autoCommit = getBoolean( AvailableSettings.AUTOCOMMIT, configurationValues ); - final Integer isolation = ConnectionProviderInitiator.extractIsolation( configurationValues ); + final Integer isolation = extractIsolation( configurationValues ); final String initSql = (String) configurationValues.get( INIT_SQL ); final ConnectionCreatorFactory factory = getConnectionCreatorFactory( configurationValues, serviceRegistry ); @@ -150,7 +152,8 @@ private static ConnectionCreator buildCreator( Boolean.toString( autoCommit ), isolation != null ? toIsolationNiceName( isolation ) : null, getInt( MIN_SIZE, configurationValues, 1 ), - getInt( AvailableSettings.POOL_SIZE, configurationValues, 20 ) + getInt( AvailableSettings.POOL_SIZE, configurationValues, 20 ), + null ); return factory.create( @@ -292,7 +295,8 @@ public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect) { dbInfo.getAutoCommitMode(), dbInfo.getIsolationLevel(), dbInfo.getPoolMinSize(), - dbInfo.getPoolMaxSize() + dbInfo.getPoolMaxSize(), + dbInfo.getJdbcFetchSize() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DataSourceBasedMultiTenantConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DataSourceBasedMultiTenantConnectionProviderImpl.java index e9257cee19c9..788f8f5a906d 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DataSourceBasedMultiTenantConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DataSourceBasedMultiTenantConnectionProviderImpl.java @@ -125,6 +125,7 @@ public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect) { null, null, null, + null, null ) { @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DatabaseConnectionInfo.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DatabaseConnectionInfo.java index e3717de1c151..191a4e964810 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DatabaseConnectionInfo.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/DatabaseConnectionInfo.java @@ -61,6 +61,12 @@ public interface DatabaseConnectionInfo { @Nullable Integer getPoolMaxSize(); + /** + * The default JDBC fetch size. + */ + @Nullable + Integer getJdbcFetchSize(); + /** * Collects the information available here as a single String with the intent of using it in logging. */ diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java index d672ed1229a6..d4897d4c60f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java @@ -8,6 +8,7 @@ import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -49,7 +50,7 @@ public class ExtractedDatabaseMetaDataImpl implements ExtractedDatabaseMetaData private final String url; private final String driver; private final boolean jdbcMetadataAccessible; - + private final int defaultFetchSize; //Lazily initialized: loading all sequence information upfront has been //shown to be too slow in some cases. In this way we only load it @@ -71,6 +72,7 @@ private ExtractedDatabaseMetaDataImpl( SQLStateType sqlStateType, int transactionIsolation, int defaultTransactionIsolation, + int defaultFetchSize, String url, String driver, boolean jdbcMetadataIsAccessible) { @@ -88,6 +90,7 @@ private ExtractedDatabaseMetaDataImpl( this.sqlStateType = sqlStateType; this.transactionIsolation = transactionIsolation; this.defaultTransactionIsolation = defaultTransactionIsolation; + this.defaultFetchSize = defaultFetchSize; this.url = url; this.driver = driver; this.jdbcMetadataAccessible = jdbcMetadataIsAccessible; @@ -168,6 +171,11 @@ public int getDefaultTransactionIsolation() { return defaultTransactionIsolation; } + @Override + public int getDefaultFetchSize() { + return defaultFetchSize; + } + @Override public synchronized List getSequenceInformationList() { if ( jdbcMetadataAccessible ) { @@ -211,6 +219,7 @@ public static class Builder { private String driver; private int defaultTransactionIsolation; private int transactionIsolation; + private int defaultFetchSize; public Builder(JdbcEnvironment jdbcEnvironment, boolean jdbcMetadataIsAccessible, JdbcConnectionAccess connectionAccess) { this.jdbcEnvironment = jdbcEnvironment; @@ -233,6 +242,12 @@ public Builder apply(DatabaseMetaData databaseMetaData) throws SQLException { driver = databaseMetaData.getDriverName(); defaultTransactionIsolation = databaseMetaData.getDefaultTransactionIsolation(); transactionIsolation = databaseMetaData.getConnection().getTransactionIsolation(); + try ( Statement statement = databaseMetaData.getConnection().createStatement() ) { + defaultFetchSize = statement.getFetchSize(); + } + catch (SQLException sqle) { + defaultFetchSize = -1; + } return this; } @@ -302,6 +317,7 @@ public ExtractedDatabaseMetaDataImpl build() { sqlStateType, transactionIsolation, defaultTransactionIsolation, + defaultFetchSize, url, driver, jdbcMetadataIsAccessible diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java index 1a6d81dafdd0..deb84a7860c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java @@ -10,6 +10,7 @@ import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.registry.selector.spi.StrategySelector; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.JdbcSettings; import org.hibernate.dialect.Dialect; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; @@ -38,6 +39,7 @@ import static org.hibernate.cfg.MappingSettings.DEFAULT_CATALOG; import static org.hibernate.cfg.MappingSettings.DEFAULT_SCHEMA; import static org.hibernate.engine.config.spi.StandardConverters.STRING; +import static org.hibernate.engine.jdbc.JdbcLogging.JDBC_MESSAGE_LOGGER; import static org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl.makeLobCreatorBuilder; /** @@ -105,6 +107,8 @@ public JdbcEnvironmentImpl(final ServiceRegistryImplementor serviceRegistry, fin new QualifiedObjectNameFormatterStandardImpl( nameQualifierSupport, dialect.getCatalogSeparator() ); lobCreatorBuilder = makeLobCreatorBuilder( dialect ); + + logJdbcFetchSize( extractedMetaDataSupport.getDefaultFetchSize(), cfgService ); } private static ConfigurationService configurationService(ServiceRegistryImplementor serviceRegistry) { @@ -317,6 +321,8 @@ public JdbcEnvironmentImpl( new QualifiedObjectNameFormatterStandardImpl( nameQualifierSupport, databaseMetaData ); lobCreatorBuilder = makeLobCreatorBuilder( dialect, cfgService.getSettings(), databaseMetaData.getConnection() ); + + logJdbcFetchSize( extractedMetaDataSupport.getDefaultFetchSize(), cfgService ); } private static IdentifierHelper identifierHelper( @@ -420,4 +426,14 @@ public LobCreatorBuilder getLobCreatorBuilder() { return lobCreatorBuilder; } + private static void logJdbcFetchSize(int defaultFetchSize, ConfigurationService cfgService) { + if ( !cfgService.getSettings().containsKey( JdbcSettings.STATEMENT_FETCH_SIZE ) ) { + if ( defaultFetchSize > 0 && defaultFetchSize < 100 ) { + JDBC_MESSAGE_LOGGER.warnLowFetchSize( defaultFetchSize ); + } + else { + JDBC_MESSAGE_LOGGER.usingFetchSize( defaultFetchSize ); + } + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/ExtractedDatabaseMetaData.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/ExtractedDatabaseMetaData.java index 6ed473de9990..dad55f55dce0 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/ExtractedDatabaseMetaData.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/ExtractedDatabaseMetaData.java @@ -150,6 +150,11 @@ public interface ExtractedDatabaseMetaData { */ int getDefaultTransactionIsolation(); + /** + * Retrieve the default JDBC {@linkplain java.sql.Statement#getFetchSize fetch size}. + */ + int getDefaultFetchSize(); + /** * Retrieve the list of {@code SequenceInformation} objects which describe the underlying database sequences. * diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparerImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparerImpl.java index 83f5e472b281..093d88c5b7fa 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparerImpl.java @@ -25,6 +25,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static org.hibernate.engine.jdbc.JdbcLogging.JDBC_MESSAGE_LOGGER; + /** * Standard implementation of {@link StatementPreparer}. * @@ -141,7 +143,7 @@ public PreparedStatement prepareQueryStatement( final int resultSetType; if ( scrollMode != null && scrollMode != ScrollMode.FORWARD_ONLY ) { if ( !settings().isScrollableResultSetsEnabled() ) { - throw new AssertionFailure("scrollable result sets are not enabled"); + throw new AssertionFailure( "Scrollable result sets are not enabled" ); } resultSetType = scrollMode.toResultSetType(); } @@ -224,8 +226,21 @@ public void postProcess(PreparedStatement preparedStatement) throws SQLException } private void setStatementFetchSize(PreparedStatement statement) throws SQLException { - if ( settings().getFetchSizeOrNull() != null ) { - statement.setFetchSize( settings().getFetchSizeOrNull() ); + final Integer fetchSize = settings().getFetchSizeOrNull(); + if ( fetchSize != null ) { + JDBC_MESSAGE_LOGGER.settingFetchSize( fetchSize ); + statement.setFetchSize( fetchSize ); + } + else { + if ( JDBC_MESSAGE_LOGGER.isDebugEnabled() ) { + final int defaultFetchSize = statement.getFetchSize(); + if ( defaultFetchSize > 0 && defaultFetchSize < 100 ) { + JDBC_MESSAGE_LOGGER.lowFetchSize( defaultFetchSize ); + } + } + else if ( JDBC_MESSAGE_LOGGER.isTraceEnabled() ) { + JDBC_MESSAGE_LOGGER.fetchSize( statement.getFetchSize() ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java index 1cf49eea848f..a36c4b2e5c48 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java @@ -36,6 +36,7 @@ import org.hibernate.sql.exec.spi.JdbcSelectExecutor; import static java.util.Collections.emptyMap; +import static org.hibernate.engine.jdbc.JdbcLogging.JDBC_MESSAGE_LOGGER; /** * @author Steve Ebersole @@ -241,11 +242,15 @@ private void setQueryOptions(PreparedStatement preparedStatement) throws SQLExce final QueryOptions queryOptions = executionContext.getQueryOptions(); // set options if ( queryOptions != null ) { - if ( queryOptions.getFetchSize() != null ) { - preparedStatement.setFetchSize( queryOptions.getFetchSize() ); + final Integer fetchSize = queryOptions.getFetchSize(); + if ( fetchSize != null ) { + JDBC_MESSAGE_LOGGER.settingFetchSize( fetchSize ); + preparedStatement.setFetchSize( fetchSize ); } - if ( queryOptions.getTimeout() != null ) { - preparedStatement.setQueryTimeout( queryOptions.getTimeout() ); + final Integer timeout = queryOptions.getTimeout(); + if ( timeout != null ) { + JDBC_MESSAGE_LOGGER.settingQueryTimeout( timeout ); + preparedStatement.setQueryTimeout( timeout ); } } } diff --git a/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java index ce87ec972ce0..8ff2c89a2194 100644 --- a/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java +++ b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java @@ -24,6 +24,9 @@ import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.toIsolationNiceName; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getFetchSize; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getIsolation; import static org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.allowJdbcMetadataAccess; import static org.hibernate.hikaricp.internal.HikariConfigurationUtil.loadConfiguration; import static org.hibernate.internal.util.StringHelper.isBlank; @@ -105,9 +108,12 @@ public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect) { : hikariConfig.getDriverClassName(), dialect.getVersion(), Boolean.toString( hikariConfig.isAutoCommit() ), - hikariConfig.getTransactionIsolation(), + hikariConfig.getTransactionIsolation() != null + ? hikariConfig.getTransactionIsolation() + : toIsolationNiceName( getIsolation( hikariDataSource ) ), hikariConfig.getMinimumIdle(), - hikariConfig.getMaximumPoolSize() + hikariConfig.getMaximumPoolSize(), + getFetchSize( hikariDataSource ) ); }