cleaner approach to work around Oracle setNull(BOOLEAN) bug
- introduce doBindNull() in BasicBinder - use WrapperOptions and FastSessionServices - use getPreferredSqlTypeCodeForBoolean()
This commit is contained in:
parent
0ecd66fd46
commit
e60e3736a7
|
@ -29,7 +29,6 @@ import org.hibernate.exception.LockTimeoutException;
|
||||||
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||||
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.internal.CoreLogging;
|
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
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.internal.SequenceInformationExtractorOracleDatabaseImpl;
|
||||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
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.BlobTypeDescriptor;
|
||||||
import org.hibernate.type.descriptor.jdbc.BooleanTypeDescriptor;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor;
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
import java.sql.CallableStatement;
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
|
@ -78,8 +70,6 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
||||||
import static org.hibernate.query.TemporalUnit.*;
|
import static org.hibernate.query.TemporalUnit.*;
|
||||||
|
|
||||||
|
@ -656,79 +646,6 @@ public class OracleDialect extends Dialect {
|
||||||
|
|
||||||
typeContributions.contributeJdbcTypeDescriptor( descriptor );
|
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 <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
|
|
||||||
return new ValueBinder<X>() {
|
|
||||||
|
|
||||||
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
|
@Override
|
||||||
|
|
|
@ -28,6 +28,11 @@ public abstract class AbstractDelegatingWrapperOptions implements WrapperOptions
|
||||||
return delegate().useStreamForLobBinding();
|
return delegate().useStreamForLobBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForBoolean() {
|
||||||
|
return delegate().getPreferredSqlTypeCodeForBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LobCreator getLobCreator() {
|
public LobCreator getLobCreator() {
|
||||||
return delegate().getLobCreator();
|
return delegate().getLobCreator();
|
||||||
|
|
|
@ -1095,6 +1095,11 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
|
||||||
return delegate.useStreamForLobBinding();
|
return delegate.useStreamForLobBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForBoolean() {
|
||||||
|
return delegate.getPreferredSqlTypeCodeForBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LobCreator getLobCreator() {
|
public LobCreator getLobCreator() {
|
||||||
return delegate.getLobCreator();
|
return delegate.getLobCreator();
|
||||||
|
|
|
@ -557,6 +557,11 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
||||||
return fastSessionServices.useStreamForLobBinding;
|
return fastSessionServices.useStreamForLobBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForBoolean() {
|
||||||
|
return fastSessionServices.preferredSqlTypeCodeForBoolean;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LobCreator getLobCreator() {
|
public LobCreator getLobCreator() {
|
||||||
return Hibernate.getLobCreator( this );
|
return Hibernate.getLobCreator( this );
|
||||||
|
|
|
@ -142,6 +142,7 @@ public final class FastSessionServices {
|
||||||
//Intentionally Package private:
|
//Intentionally Package private:
|
||||||
final boolean disallowOutOfTransactionUpdateOperations;
|
final boolean disallowOutOfTransactionUpdateOperations;
|
||||||
final boolean useStreamForLobBinding;
|
final boolean useStreamForLobBinding;
|
||||||
|
final int preferredSqlTypeCodeForBoolean;
|
||||||
final boolean requiresMultiTenantConnectionProvider;
|
final boolean requiresMultiTenantConnectionProvider;
|
||||||
final ConnectionProvider connectionProvider;
|
final ConnectionProvider connectionProvider;
|
||||||
final MultiTenantConnectionProvider multiTenantConnectionProvider;
|
final MultiTenantConnectionProvider multiTenantConnectionProvider;
|
||||||
|
@ -210,6 +211,7 @@ public final class FastSessionServices {
|
||||||
this.dialect = jdbcServices.getJdbcEnvironment().getDialect();
|
this.dialect = jdbcServices.getJdbcEnvironment().getDialect();
|
||||||
this.disallowOutOfTransactionUpdateOperations = !sessionFactoryOptions.isAllowOutOfTransactionUpdateOperations();
|
this.disallowOutOfTransactionUpdateOperations = !sessionFactoryOptions.isAllowOutOfTransactionUpdateOperations();
|
||||||
this.useStreamForLobBinding = Environment.useStreamsForBinary() || dialect.useInputStreamToInsertBlob();
|
this.useStreamForLobBinding = Environment.useStreamsForBinary() || dialect.useInputStreamToInsertBlob();
|
||||||
|
this.preferredSqlTypeCodeForBoolean = sessionFactoryOptions.getPreferredSqlTypeCodeForBoolean();
|
||||||
this.requiresMultiTenantConnectionProvider = sf.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider();
|
this.requiresMultiTenantConnectionProvider = sf.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider();
|
||||||
|
|
||||||
//Some "hot" services:
|
//Some "hot" services:
|
||||||
|
@ -343,4 +345,8 @@ public final class FastSessionServices {
|
||||||
public void firePostLoadEvent(final PostLoadEvent postLoadEvent) {
|
public void firePostLoadEvent(final PostLoadEvent postLoadEvent) {
|
||||||
eventListenerGroup_POST_LOAD.fireEventOnEachListener( postLoadEvent, PostLoadEventListener::onPostLoad );
|
eventListenerGroup_POST_LOAD.fireEventOnEachListener( postLoadEvent, PostLoadEventListener::onPostLoad );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPreferredSqlTypeCodeForBoolean() {
|
||||||
|
return preferredSqlTypeCodeForBoolean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,6 @@ import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||||
import org.hibernate.persister.entity.Loadable;
|
import org.hibernate.persister.entity.Loadable;
|
||||||
import org.hibernate.query.ComparisonOperator;
|
import org.hibernate.query.ComparisonOperator;
|
||||||
import org.hibernate.query.FetchClauseType;
|
|
||||||
import org.hibernate.query.Limit;
|
import org.hibernate.query.Limit;
|
||||||
import org.hibernate.query.NullPrecedence;
|
import org.hibernate.query.NullPrecedence;
|
||||||
import org.hibernate.query.SortOrder;
|
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.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteColumn;
|
import org.hibernate.sql.ast.tree.cte.CteColumn;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteContainer;
|
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.CteStatement;
|
||||||
import org.hibernate.sql.ast.tree.cte.SearchClauseSpecification;
|
import org.hibernate.sql.ast.tree.cte.SearchClauseSpecification;
|
||||||
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
||||||
|
@ -268,6 +266,11 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
return sessionFactory.getFastSessionServices().useStreamForLobBinding();
|
return sessionFactory.getFastSessionServices().useStreamForLobBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForBoolean() {
|
||||||
|
return sessionFactory.getFastSessionServices().getPreferredSqlTypeCodeForBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JdbcTypeDescriptor remapSqlTypeDescriptor(JdbcTypeDescriptor jdbcTypeDescriptor) {
|
public JdbcTypeDescriptor remapSqlTypeDescriptor(JdbcTypeDescriptor jdbcTypeDescriptor) {
|
||||||
return sessionFactory.getFastSessionServices().remapSqlTypeDescriptor( jdbcTypeDescriptor );
|
return sessionFactory.getFastSessionServices().remapSqlTypeDescriptor( jdbcTypeDescriptor );
|
||||||
|
|
|
@ -33,6 +33,11 @@ public interface WrapperOptions {
|
||||||
*/
|
*/
|
||||||
boolean useStreamForLobBinding();
|
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}
|
* Obtain access to the {@link LobCreator}
|
||||||
*
|
*
|
||||||
|
|
|
@ -55,7 +55,7 @@ public abstract class BasicBinder<J> implements ValueBinder<J> {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
st.setNull( index, jdbcTypeDescriptor.getJdbcType() );
|
doBindNull( st, index, options );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( JdbcBindingLogging.TRACE_ENABLED ) {
|
if ( JdbcBindingLogging.TRACE_ENABLED ) {
|
||||||
|
@ -84,7 +84,7 @@ public abstract class BasicBinder<J> implements ValueBinder<J> {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
st.setNull( name, jdbcTypeDescriptor.getJdbcType() );
|
doBindNull( st, name, options );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( JdbcBindingLogging.TRACE_ENABLED ) {
|
if ( JdbcBindingLogging.TRACE_ENABLED ) {
|
||||||
|
@ -101,6 +101,32 @@ public abstract class BasicBinder<J> implements ValueBinder<J> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Perform the binding. Safe to assume that value is not null.
|
||||||
*
|
*
|
||||||
|
@ -122,7 +148,7 @@ public abstract class BasicBinder<J> implements ValueBinder<J> {
|
||||||
* @param name The name at which to bind
|
* @param name The name at which to bind
|
||||||
* @param options The binding options
|
* @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)
|
protected abstract void doBind(CallableStatement st, J value, String name, WrapperOptions options)
|
||||||
throws SQLException;
|
throws SQLException;
|
||||||
|
|
|
@ -63,6 +63,16 @@ public class BooleanTypeDescriptor implements JdbcTypeDescriptor {
|
||||||
|
|
||||||
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
|
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
|
||||||
return new BasicBinder<X>( javaTypeDescriptor, this ) {
|
return new BasicBinder<X>( 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
|
@Override
|
||||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
|
||||||
st.setBoolean( index, javaTypeDescriptor.unwrap( value, Boolean.class, options ) );
|
st.setBoolean( index, javaTypeDescriptor.unwrap( value, Boolean.class, options ) );
|
||||||
|
|
|
@ -12,6 +12,7 @@ import java.lang.reflect.Proxy;
|
||||||
import java.sql.CallableStatement;
|
import java.sql.CallableStatement;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Types;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.hibernate.engine.jdbc.LobCreator;
|
import org.hibernate.engine.jdbc.LobCreator;
|
||||||
|
@ -98,6 +99,11 @@ public class MaterializedNClobBindTest {
|
||||||
return useStreamForLobBinding;
|
return useStreamForLobBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForBoolean() {
|
||||||
|
return Types.BOOLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LobCreator getLobCreator() {
|
public LobCreator getLobCreator() {
|
||||||
return NonContextualLobCreator.INSTANCE;
|
return NonContextualLobCreator.INSTANCE;
|
||||||
|
|
Loading…
Reference in New Issue