From e60e3736a7259b6a69b42d5ddee946f0df2ee0a9 Mon Sep 17 00:00:00 2001 From: gavin Date: Fri, 19 Mar 2021 07:44:11 +0100 Subject: [PATCH] cleaner approach to work around Oracle setNull(BOOLEAN) bug - introduce doBindNull() in BasicBinder - use WrapperOptions and FastSessionServices - use getPreferredSqlTypeCodeForBoolean() --- .../org/hibernate/dialect/OracleDialect.java | 83 ------------------- .../spi/AbstractDelegatingWrapperOptions.java | 5 ++ .../engine/spi/SessionDelegatorBaseImpl.java | 5 ++ .../AbstractSharedSessionContract.java | 5 ++ .../internal/FastSessionServices.java | 6 ++ .../sql/ast/spi/AbstractSqlAstTranslator.java | 7 +- .../type/descriptor/WrapperOptions.java | 5 ++ .../type/descriptor/jdbc/BasicBinder.java | 32 ++++++- .../jdbc/BooleanTypeDescriptor.java | 10 +++ .../MaterializedNClobBindTest.java | 6 ++ 10 files changed, 76 insertions(+), 88 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 fbc9a46727..3a08d15cd2 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -29,7 +29,6 @@ import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; -import org.hibernate.internal.CoreLogging; import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.StringHelper; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -57,18 +56,11 @@ import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorOracleDatabaseImpl; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; import org.hibernate.type.StandardBasicTypes; -import org.hibernate.type.descriptor.JdbcTypeNameMapper; -import org.hibernate.type.descriptor.ValueBinder; -import org.hibernate.type.descriptor.WrapperOptions; -import org.hibernate.type.descriptor.java.JavaTypeDescriptor; -import org.hibernate.type.descriptor.jdbc.BasicBinder; import org.hibernate.type.descriptor.jdbc.BlobTypeDescriptor; -import org.hibernate.type.descriptor.jdbc.BooleanTypeDescriptor; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; import java.sql.CallableStatement; -import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; @@ -78,8 +70,6 @@ import java.util.regex.Pattern; import javax.persistence.TemporalType; -import org.jboss.logging.Logger; - import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.query.TemporalUnit.*; @@ -656,79 +646,6 @@ public class OracleDialect extends Dialect { typeContributions.contributeJdbcTypeDescriptor( descriptor ); } - typeContributions.contributeJdbcTypeDescriptor( new OracleBooleanTypeDescriptor() ); - } - - private static final class OracleBooleanTypeDescriptor extends BooleanTypeDescriptor { - - private static final Logger log = CoreLogging.logger( BasicBinder.class ); - - @Override - public ValueBinder getBinder(JavaTypeDescriptor javaTypeDescriptor) { - return new ValueBinder() { - - private static final String BIND_MSG_TEMPLATE = "binding parameter [%s] as [%s] - [%s]"; - private static final String NULL_BIND_MSG_TEMPLATE = "binding parameter [%s] as [%s] - [null]"; - - @Override - public final void bind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException { - if ( value == null ) { - if ( log.isTraceEnabled() ) { - log.trace( - String.format( - NULL_BIND_MSG_TEMPLATE, - index, - JdbcTypeNameMapper.getTypeName( Types.BOOLEAN ) - ) - ); - } - st.setNull( index, Types.BIT ); - } - else { - if ( log.isTraceEnabled() ) { - log.trace( - String.format( - BIND_MSG_TEMPLATE, - index, - JdbcTypeNameMapper.getTypeName( Types.BOOLEAN ), - javaTypeDescriptor.extractLoggableRepresentation( value ) - ) - ); - } - st.setBoolean( index, javaTypeDescriptor.unwrap( value, Boolean.class, options ) ); - } - } - - @Override - public final void bind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException { - if ( value == null ) { - if ( log.isTraceEnabled() ) { - log.trace( - String.format( - NULL_BIND_MSG_TEMPLATE, - name, - JdbcTypeNameMapper.getTypeName( Types.BOOLEAN ) - ) - ); - } - st.setNull( name, Types.BIT ); - } - else { - if ( log.isTraceEnabled() ) { - log.trace( - String.format( - BIND_MSG_TEMPLATE, - name, - JdbcTypeNameMapper.getTypeName( Types.BOOLEAN ), - javaTypeDescriptor.extractLoggableRepresentation( value ) - ) - ); - } - st.setBoolean( name, javaTypeDescriptor.unwrap( value, Boolean.class, options ) ); - } - } - }; - } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingWrapperOptions.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingWrapperOptions.java index ff3efe387a..6f7b671df7 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingWrapperOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/AbstractDelegatingWrapperOptions.java @@ -28,6 +28,11 @@ public abstract class AbstractDelegatingWrapperOptions implements WrapperOptions return delegate().useStreamForLobBinding(); } + @Override + public int getPreferredSqlTypeCodeForBoolean() { + return delegate().getPreferredSqlTypeCodeForBoolean(); + } + @Override public LobCreator getLobCreator() { return delegate().getLobCreator(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java index 2eeb7e1bdb..bcb5c20ef1 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java @@ -1095,6 +1095,11 @@ public class SessionDelegatorBaseImpl implements SessionImplementor { return delegate.useStreamForLobBinding(); } + @Override + public int getPreferredSqlTypeCodeForBoolean() { + return delegate.getPreferredSqlTypeCodeForBoolean(); + } + @Override public LobCreator getLobCreator() { return delegate.getLobCreator(); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index c8b4bb50b2..eddf2ca4f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -557,6 +557,11 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont return fastSessionServices.useStreamForLobBinding; } + @Override + public int getPreferredSqlTypeCodeForBoolean() { + return fastSessionServices.preferredSqlTypeCodeForBoolean; + } + @Override public LobCreator getLobCreator() { return Hibernate.getLobCreator( this ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index 21b7228814..c9c0705a32 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -142,6 +142,7 @@ public final class FastSessionServices { //Intentionally Package private: final boolean disallowOutOfTransactionUpdateOperations; final boolean useStreamForLobBinding; + final int preferredSqlTypeCodeForBoolean; final boolean requiresMultiTenantConnectionProvider; final ConnectionProvider connectionProvider; final MultiTenantConnectionProvider multiTenantConnectionProvider; @@ -210,6 +211,7 @@ public final class FastSessionServices { this.dialect = jdbcServices.getJdbcEnvironment().getDialect(); this.disallowOutOfTransactionUpdateOperations = !sessionFactoryOptions.isAllowOutOfTransactionUpdateOperations(); this.useStreamForLobBinding = Environment.useStreamsForBinary() || dialect.useInputStreamToInsertBlob(); + this.preferredSqlTypeCodeForBoolean = sessionFactoryOptions.getPreferredSqlTypeCodeForBoolean(); this.requiresMultiTenantConnectionProvider = sf.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider(); //Some "hot" services: @@ -343,4 +345,8 @@ public final class FastSessionServices { public void firePostLoadEvent(final PostLoadEvent postLoadEvent) { eventListenerGroup_POST_LOAD.fireEventOnEachListener( postLoadEvent, PostLoadEventListener::onPostLoad ); } + + public int getPreferredSqlTypeCodeForBoolean() { + return preferredSqlTypeCodeForBoolean; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 2da3c7c1d7..dafeed8b0f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -49,7 +49,6 @@ import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.persister.entity.Loadable; import org.hibernate.query.ComparisonOperator; -import org.hibernate.query.FetchClauseType; import org.hibernate.query.Limit; import org.hibernate.query.NullPrecedence; import org.hibernate.query.SortOrder; @@ -69,7 +68,6 @@ import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteColumn; import org.hibernate.sql.ast.tree.cte.CteContainer; -import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind; import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.cte.SearchClauseSpecification; import org.hibernate.sql.ast.tree.delete.DeleteStatement; @@ -268,6 +266,11 @@ public abstract class AbstractSqlAstTranslator implemen return sessionFactory.getFastSessionServices().useStreamForLobBinding(); } + @Override + public int getPreferredSqlTypeCodeForBoolean() { + return sessionFactory.getFastSessionServices().getPreferredSqlTypeCodeForBoolean(); + } + @Override public JdbcTypeDescriptor remapSqlTypeDescriptor(JdbcTypeDescriptor jdbcTypeDescriptor) { return sessionFactory.getFastSessionServices().remapSqlTypeDescriptor( jdbcTypeDescriptor ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/WrapperOptions.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/WrapperOptions.java index dc95031491..73921815b5 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/WrapperOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/WrapperOptions.java @@ -33,6 +33,11 @@ public interface WrapperOptions { */ boolean useStreamForLobBinding(); + /** + * Get the JDBC {@link java.sql.Types type code} used to bind a null boolean value + */ + int getPreferredSqlTypeCodeForBoolean(); + /** * Obtain access to the {@link LobCreator} * diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BasicBinder.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BasicBinder.java index f5f8a259cb..3edecf4fbf 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BasicBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BasicBinder.java @@ -55,7 +55,7 @@ public abstract class BasicBinder implements ValueBinder { ) ); } - st.setNull( index, jdbcTypeDescriptor.getJdbcType() ); + doBindNull( st, index, options ); } else { if ( JdbcBindingLogging.TRACE_ENABLED ) { @@ -84,7 +84,7 @@ public abstract class BasicBinder implements ValueBinder { ) ); } - st.setNull( name, jdbcTypeDescriptor.getJdbcType() ); + doBindNull( st, name, options ); } else { if ( JdbcBindingLogging.TRACE_ENABLED ) { @@ -101,6 +101,32 @@ public abstract class BasicBinder implements ValueBinder { } } + /** + * Perform the null binding. + * + * @param st The prepared statement + * @param index The index at which to bind + * @param options The binding options + * + * @throws SQLException Indicates a problem binding to the prepared statement. + */ + protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException { + st.setNull( index, jdbcTypeDescriptor.getJdbcType() ); + } + + /** + * Perform the null binding. + * + * @param st The CallableStatement + * @param name The name at which to bind + * @param options The binding options + * + * @throws SQLException Indicates a problem binding to the callable statement. + */ + protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException { + st.setNull( name, jdbcTypeDescriptor.getJdbcType() ); + } + /** * Perform the binding. Safe to assume that value is not null. * @@ -122,7 +148,7 @@ public abstract class BasicBinder implements ValueBinder { * @param name The name at which to bind * @param options The binding options * - * @throws SQLException Indicates a problem binding to the prepared statement. + * @throws SQLException Indicates a problem binding to the callable statement. */ protected abstract void doBind(CallableStatement st, J value, String name, WrapperOptions options) throws SQLException; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BooleanTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BooleanTypeDescriptor.java index c4284ededf..8aa170e2d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BooleanTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BooleanTypeDescriptor.java @@ -63,6 +63,16 @@ public class BooleanTypeDescriptor implements JdbcTypeDescriptor { public ValueBinder getBinder(final JavaTypeDescriptor javaTypeDescriptor) { return new BasicBinder( javaTypeDescriptor, this ) { + @Override + protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException { + st.setNull( index, options.getPreferredSqlTypeCodeForBoolean() ); + } + + @Override + protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException { + st.setNull( name, options.getPreferredSqlTypeCodeForBoolean() );; + } + @Override protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException { st.setBoolean( index, javaTypeDescriptor.unwrap( value, Boolean.class, options ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/nationalized/MaterializedNClobBindTest.java b/hibernate-core/src/test/java/org/hibernate/test/nationalized/MaterializedNClobBindTest.java index 0e42f7bb22..96d6b8b8ea 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/nationalized/MaterializedNClobBindTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/nationalized/MaterializedNClobBindTest.java @@ -12,6 +12,7 @@ import java.lang.reflect.Proxy; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Types; import java.util.TimeZone; import org.hibernate.engine.jdbc.LobCreator; @@ -98,6 +99,11 @@ public class MaterializedNClobBindTest { return useStreamForLobBinding; } + @Override + public int getPreferredSqlTypeCodeForBoolean() { + return Types.BOOLEAN; + } + @Override public LobCreator getLobCreator() { return NonContextualLobCreator.INSTANCE;