From 501b57a978bb8dc1208fe903be8bf6ef6d9c96a8 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 14 Nov 2023 14:48:01 -0600 Subject: [PATCH] HHH-17424 - Have Dialect manage more of ExtractedDatabaseMetadata https://hibernate.atlassian.net/browse/HHH-17424 --- .../SessionFactoryOptionsBuilder.java | 17 +++- .../java/org/hibernate/dialect/Dialect.java | 24 +++++ .../internal/FallbackRefCursorSupport.java | 71 +++++++++++++++ .../internal/RefCursorSupportInitiator.java | 19 +++- .../internal/StandardRefCursorSupport.java | 90 ++++--------------- .../env/spi/ExtractedDatabaseMetaData.java | 15 +++- 6 files changed, 159 insertions(+), 77 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/FallbackRefCursorSupport.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java index 8b00e2fb8d..7e0cc2334f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java @@ -275,8 +275,13 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class ); final JdbcServices jdbcServices = serviceRegistry.getService( JdbcServices.class ); + assert jdbcServices != null; + assert configurationService != null; + + final Dialect dialect = jdbcServices.getJdbcEnvironment().getDialect(); + final Map configurationSettings = new HashMap<>(); - configurationSettings.putAll( map( jdbcServices.getJdbcEnvironment().getDialect().getDefaultProperties() ) ); + configurationSettings.putAll( map( dialect.getDefaultProperties() ) ); configurationSettings.putAll( configurationService.getSettings() ); this.beanManagerReference = NullnessHelper.coalesceSuppliedValues( @@ -488,7 +493,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { } this.jdbcBatchSize = getInt( STATEMENT_BATCH_SIZE, configurationSettings, 1 ); - if ( !meta.supportsBatchUpdates() ) { + if ( disallowBatchUpdates( dialect, meta ) ) { this.jdbcBatchSize = 0; } @@ -585,6 +590,14 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { ); } + private boolean disallowBatchUpdates(Dialect dialect, ExtractedDatabaseMetaData meta) { + final Boolean dialectAnswer = dialect.supportsBatchUpdates(); + if ( dialectAnswer != null ) { + return dialectAnswer; + } + return !meta.supportsBatchUpdates(); + } + @SuppressWarnings("unchecked") private SqmMultiTableMutationStrategy resolveSqmMutationStrategy( String strategyName, diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index c38e61e618..a4f1cb8afa 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -4952,6 +4952,30 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun return null; } + /** + * Does this Dialect support {@linkplain PreparedStatement#addBatch() batch updates}. + * + * @return {@code true} indicates it does; {@code false} indicates it does not; {@code null} indicates + * it might and that database-metadata should be consulted. + * + * @see org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData#supportsBatchUpdates + */ + public Boolean supportsBatchUpdates() { + return null; + } + + /** + * Does this Dialect support {@linkplain PreparedStatement#addBatch() batch updates}. + * + * @return {@code true} indicates it does; {@code false} indicates it does not; {@code null} indicates + * it might and that database-metadata should be consulted + * + * @see org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData#supportsRefCursors + */ + public Boolean supportsRefCursors() { + return null; + } + /** * Pluggable strategy for determining the {@link Size} to use for * columns of a given SQL type. diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/FallbackRefCursorSupport.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/FallbackRefCursorSupport.java new file mode 100644 index 0000000000..0f00bff757 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/FallbackRefCursorSupport.java @@ -0,0 +1,71 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.engine.jdbc.cursor.internal; + +import java.sql.CallableStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport; +import org.hibernate.engine.jdbc.spi.JdbcServices; + +/** + * @author Steve Ebersole + */ +public class FallbackRefCursorSupport implements RefCursorSupport { + private final JdbcServices jdbcServices; + + public FallbackRefCursorSupport(JdbcServices jdbcServices) { + this.jdbcServices = jdbcServices; + } + + @Override + public void registerRefCursorParameter(CallableStatement statement, int position) { + try { + jdbcServices.getDialect().registerResultSetOutParameter( statement, position ); + } + catch (SQLException e) { + throw jdbcServices.getSqlExceptionHelper().convert( e, "Error asking dialect to register ref cursor parameter [" + position + "]" ); + } + } + + @Override + public void registerRefCursorParameter(CallableStatement statement, String name) { + try { + jdbcServices.getDialect().registerResultSetOutParameter( statement, name ); + } + catch (SQLException e) { + throw jdbcServices.getSqlExceptionHelper().convert( e, "Error asking dialect to register ref cursor parameter [" + name + "]" ); + } + } + + @Override + public ResultSet getResultSet(CallableStatement statement, int position) { + try { + return jdbcServices.getDialect().getResultSet( statement, position ); + } + catch (SQLException e) { + throw jdbcServices.getSqlExceptionHelper().convert( + e, + "Error asking dialect to extract ResultSet from CallableStatement parameter [" + position + "]" + ); + } + } + + @Override + public ResultSet getResultSet(CallableStatement statement, String name) { + try { + return jdbcServices.getDialect().getResultSet( statement, name ); + } + catch (SQLException e) { + throw jdbcServices.getSqlExceptionHelper().convert( + e, + "Error asking dialect to extract ResultSet from CallableStatement parameter [" + name + "]" + ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/RefCursorSupportInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/RefCursorSupportInitiator.java index b6f90129da..60c8972ad7 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/RefCursorSupportInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/RefCursorSupportInitiator.java @@ -10,6 +10,7 @@ import java.util.Map; import org.hibernate.boot.registry.StandardServiceInitiator; import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport; +import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.service.spi.ServiceRegistryImplementor; /** @@ -25,7 +26,23 @@ public class RefCursorSupportInitiator implements StandardServiceInitiator configurationValues, ServiceRegistryImplementor registry) { - return new StandardRefCursorSupport(); + final JdbcServices jdbcServices = registry.getService( JdbcServices.class ); + assert jdbcServices != null; + final boolean supportsRefCursors = useRefCursorSupport( jdbcServices ); + if ( supportsRefCursors ) { + return new StandardRefCursorSupport( jdbcServices ); + } + else { + return new FallbackRefCursorSupport( jdbcServices ); + } + } + + private boolean useRefCursorSupport(JdbcServices jdbcServices) { + final Boolean dialectAnswer = jdbcServices.getDialect().supportsRefCursors(); + if ( dialectAnswer != null ) { + return dialectAnswer; + } + return jdbcServices.getJdbcEnvironment().getExtractedDatabaseMetaData().supportsRefCursors(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/StandardRefCursorSupport.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/StandardRefCursorSupport.java index 66f2d22b33..1a3b1833b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/StandardRefCursorSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/cursor/internal/StandardRefCursorSupport.java @@ -15,7 +15,6 @@ import java.sql.Types; import org.hibernate.HibernateException; import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport; import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.service.spi.InjectService; import org.jboss.logging.Logger; @@ -27,102 +26,49 @@ import org.jboss.logging.Logger; public class StandardRefCursorSupport implements RefCursorSupport { private static final Logger log = Logger.getLogger( StandardRefCursorSupport.class ); - private JdbcServices jdbcServices; + private final JdbcServices jdbcServices; - /** - * Hook for service registry to be able to inject JdbcServices - * - * @param jdbcServices The JdbcServices service - */ - @InjectService - @SuppressWarnings("UnusedDeclaration") - public void injectJdbcServices(JdbcServices jdbcServices) { + public StandardRefCursorSupport(JdbcServices jdbcServices) { this.jdbcServices = jdbcServices; } @Override public void registerRefCursorParameter(CallableStatement statement, int position) { - if ( jdbcServices.getExtractedMetaDataSupport().supportsRefCursors() ) { - try { - statement.registerOutParameter( position, refCursorTypeCode() ); - } - catch (SQLException e) { - throw jdbcServices.getSqlExceptionHelper().convert( e, "Error registering REF_CURSOR parameter [" + position + "]" ); - } + try { + statement.registerOutParameter( position, refCursorTypeCode() ); } - else { - try { - jdbcServices.getDialect().registerResultSetOutParameter( statement, position ); - } - catch (SQLException e) { - throw jdbcServices.getSqlExceptionHelper().convert( e, "Error asking dialect to register ref cursor parameter [" + position + "]" ); - } + catch (SQLException e) { + throw jdbcServices.getSqlExceptionHelper().convert( e, "Error registering REF_CURSOR parameter [" + position + "]" ); } } @Override public void registerRefCursorParameter(CallableStatement statement, String name) { - if ( jdbcServices.getExtractedMetaDataSupport().supportsRefCursors() ) { - try { - statement.registerOutParameter( name, refCursorTypeCode() ); - } - catch (SQLException e) { - throw jdbcServices.getSqlExceptionHelper().convert( e, "Error registering REF_CURSOR parameter [" + name + "]" ); - } + try { + statement.registerOutParameter( name, refCursorTypeCode() ); } - else { - try { - jdbcServices.getDialect().registerResultSetOutParameter( statement, name ); - } - catch (SQLException e) { - throw jdbcServices.getSqlExceptionHelper().convert( e, "Error asking dialect to register ref cursor parameter [" + name + "]" ); - } + catch (SQLException e) { + throw jdbcServices.getSqlExceptionHelper().convert( e, "Error registering REF_CURSOR parameter [" + name + "]" ); } } @Override public ResultSet getResultSet(CallableStatement statement, int position) { - if ( jdbcServices.getExtractedMetaDataSupport().supportsRefCursors() ) { - try { - return statement.getObject( position, ResultSet.class ); - } - catch (Exception e) { - throw new HibernateException( "Unexpected error extracting REF_CURSOR parameter [" + position + "]", e ); - } + try { + return statement.getObject( position, ResultSet.class ); } - else { - try { - return jdbcServices.getDialect().getResultSet( statement, position ); - } - catch (SQLException e) { - throw jdbcServices.getSqlExceptionHelper().convert( - e, - "Error asking dialect to extract ResultSet from CallableStatement parameter [" + position + "]" - ); - } + catch (Exception e) { + throw new HibernateException( "Unexpected error extracting REF_CURSOR parameter [" + position + "]", e ); } } @Override public ResultSet getResultSet(CallableStatement statement, String name) { - if ( jdbcServices.getExtractedMetaDataSupport().supportsRefCursors() ) { - try { - return statement.getObject( name, ResultSet.class ); - } - catch (Exception e) { - throw new HibernateException( "Unexpected error extracting REF_CURSOR parameter [" + name + "]", e ); - } + try { + return statement.getObject( name, ResultSet.class ); } - else { - try { - return jdbcServices.getDialect().getResultSet( statement, name ); - } - catch (SQLException e) { - throw jdbcServices.getSqlExceptionHelper().convert( - e, - "Error asking dialect to extract ResultSet from CallableStatement parameter [" + name + "]" - ); - } + catch (Exception e) { + throw new HibernateException( "Unexpected error extracting REF_CURSOR parameter [" + name + "]", e ); } } 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 df11225a45..c8818ebd0f 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 @@ -9,6 +9,7 @@ package org.hibernate.engine.jdbc.env.spi; import java.util.Collections; import java.util.List; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.tool.schema.extract.spi.SequenceInformation; /** @@ -30,6 +31,8 @@ public interface ExtractedDatabaseMetaData { * Retrieve the name of the catalog in effect when we connected to the database. * * @return The catalog name + * + * @see AvailableSettings#DEFAULT_SCHEMA */ String getConnectionCatalogName(); @@ -45,14 +48,19 @@ public interface ExtractedDatabaseMetaData { * * @return {@code true} indicates the driver reported true; {@code false} indicates the driver reported false * or that the driver could not be asked. + * + * @see AvailableSettings#CALLABLE_NAMED_PARAMS_ENABLED */ boolean supportsNamedParameters(); /** - * Does the driver report supporting REF_CURSORs? + * Does the driver report supporting {@link java.sql.Types#REF_CURSOR}? * * @return {@code true} indicates the driver reported true; {@code false} indicates the driver reported false * or that the driver could not be asked. + * + * @see java.sql.DatabaseMetaData#supportsRefCursors() + * @see org.hibernate.dialect.Dialect#supportsRefCursors */ boolean supportsRefCursors(); @@ -62,15 +70,17 @@ public interface ExtractedDatabaseMetaData { * @return True if the driver reported to support {@link java.sql.ResultSet#TYPE_SCROLL_INSENSITIVE}. * * @see java.sql.DatabaseMetaData#supportsResultSetType + * @see AvailableSettings#USE_SCROLLABLE_RESULTSET */ boolean supportsScrollableResults(); /** * Did the driver report to supporting retrieval of generated keys? * - * @return True if the if the driver reported to support calls to {@link java.sql.Statement#getGeneratedKeys} + * @return True if the driver reported to support calls to {@link java.sql.Statement#getGeneratedKeys} * * @see java.sql.DatabaseMetaData#supportsGetGeneratedKeys + * @see AvailableSettings#USE_GET_GENERATED_KEYS */ boolean supportsGetGeneratedKeys(); @@ -80,6 +90,7 @@ public interface ExtractedDatabaseMetaData { * @return True if the driver supports batched updates * * @see java.sql.DatabaseMetaData#supportsBatchUpdates + * @see org.hibernate.dialect.Dialect#supportsBatchUpdates */ boolean supportsBatchUpdates();