From 7a2345f8ff7518891533c9000e23c21602fdc690 Mon Sep 17 00:00:00 2001
From: Gavin King <gavin@hibernate.org>
Date: Thu, 16 Jan 2025 22:53:42 +0100
Subject: [PATCH] possible mitigation for Oracle fetch size issue

---
 .../ExtractedDatabaseMetaDataImpl.java         | 18 +++++++++++++++++-
 .../jdbc/env/internal/JdbcEnvironmentImpl.java | 15 +++++++++++++++
 .../env/spi/ExtractedDatabaseMetaData.java     |  5 +++++
 3 files changed, 37 insertions(+), 1 deletion(-)

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 09357e475eca..48b4e1baecd7 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<SequenceInformation> 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 e75275618073..6e6d3ee6c6f7 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;
@@ -103,6 +104,8 @@ public JdbcEnvironmentImpl(final ServiceRegistryImplementor serviceRegistry, fin
 		this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl( nameQualifierSupport );
 
 		this.lobCreatorBuilder = makeLobCreatorBuilder( dialect );
+
+		logJdbcFetchSize( extractedMetaDataSupport.getDefaultFetchSize(), cfgService );
 	}
 
 	private IdentifierHelperBuilder identifierHelperBuilder(
@@ -312,6 +315,8 @@ public JdbcEnvironmentImpl(
 				cfgService.getSettings(),
 				databaseMetaData.getConnection()
 		);
+
+		logJdbcFetchSize( extractedMetaDataSupport.getDefaultFetchSize(), cfgService );
 	}
 
 	private static IdentifierHelper identifierHelper(
@@ -420,4 +425,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 < 200 ) {
+				log.warn( "Low default JDBC fetch size: " + defaultFetchSize + " (set hibernate.jdbc.fetch_size)" );
+			}
+			else {
+				log.debug( "Using default JDBC fetch size: " + 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 48182669e367..0bfb244ee5c1 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 {@link java.sql.Statement#getFetchSize fetch size}.
+	 */
+	int getDefaultFetchSize();
+
 	/**
 	 * Retrieve the list of {@code SequenceInformation} objects which describe the underlying database sequences.
 	 *