HHH-9548 - Allow propagation of NULL for stored-procedure argument parameters to database

This commit is contained in:
Steve Ebersole 2016-02-09 16:08:22 -06:00
parent 19d86c3d65
commit 13bbddfd30
5 changed files with 81 additions and 17 deletions

View File

@ -17,7 +17,7 @@ public interface ParameterBind<T> {
* *
* @return The bound value. * @return The bound value.
*/ */
public T getValue(); T getValue();
/** /**
* If {@code <T>} represents a DATE/TIME type value, JPA usually allows specifying the particular parts of * If {@code <T>} represents a DATE/TIME type value, JPA usually allows specifying the particular parts of
@ -25,5 +25,5 @@ public interface ParameterBind<T> {
* *
* @return The explicitly supplied TemporalType. * @return The explicitly supplied TemporalType.
*/ */
public TemporalType getExplicitTemporalType(); TemporalType getExplicitTemporalType();
} }

View File

@ -35,6 +35,16 @@ public interface ParameterRegistrationImplementor<T> extends ParameterRegistrati
*/ */
Type getHibernateType(); Type getHibernateType();
/**
* If no value is bound for this parameter registration, is the passing of NULL
* to the JDBC CallableStatement for that parameter enabled? This effectively controls
* whether default values for the argument as defined in the database are applied or not.
*
* @return {@code true} indicates that NULL will be passed to the JDBC driver, effectively disabling
* the application of the default argument value defined in the database; {@code false} indicates
* that the parameter will simply be ignored, with the assumption that the corresponding argument
* defined a default value.
*/
boolean isPassNullsEnabled(); boolean isPassNullsEnabled();
/** /**

View File

@ -28,12 +28,13 @@ import org.hibernate.LockMode;
import org.hibernate.jpa.spi.BaseQueryImpl; import org.hibernate.jpa.spi.BaseQueryImpl;
import org.hibernate.jpa.spi.HibernateEntityManagerImplementor; import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
import org.hibernate.jpa.spi.ParameterBind; import org.hibernate.jpa.spi.ParameterBind;
import org.hibernate.jpa.spi.ParameterRegistration; import org.hibernate.jpa.spi.StoredProcedureQueryParameterRegistration;
import org.hibernate.procedure.NoSuchParameterException; import org.hibernate.procedure.NoSuchParameterException;
import org.hibernate.procedure.ParameterStrategyException; import org.hibernate.procedure.ParameterStrategyException;
import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCall;
import org.hibernate.procedure.ProcedureCallMemento; import org.hibernate.procedure.ProcedureCallMemento;
import org.hibernate.procedure.ProcedureOutputs; import org.hibernate.procedure.ProcedureOutputs;
import org.hibernate.procedure.spi.ParameterRegistrationImplementor;
import org.hibernate.result.NoMoreReturnsException; import org.hibernate.result.NoMoreReturnsException;
import org.hibernate.result.Output; import org.hibernate.result.Output;
import org.hibernate.result.ResultSetOutput; import org.hibernate.result.ResultSetOutput;
@ -62,7 +63,12 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
super( entityManager ); super( entityManager );
this.procedureCall = memento.makeProcedureCall( entityManager.getSession() ); this.procedureCall = memento.makeProcedureCall( entityManager.getSession() );
for ( org.hibernate.procedure.ParameterRegistration nativeParamReg : procedureCall.getRegisteredParameters() ) { for ( org.hibernate.procedure.ParameterRegistration nativeParamReg : procedureCall.getRegisteredParameters() ) {
registerParameter( new ParameterRegistrationImpl( this, nativeParamReg ) ); registerParameter(
new ParameterRegistrationImpl(
this,
(ParameterRegistrationImplementor) nativeParamReg
)
);
} }
} }
@ -111,7 +117,7 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
registerParameter( registerParameter(
new ParameterRegistrationImpl( new ParameterRegistrationImpl(
this, this,
procedureCall.registerParameter( position, type, mode ) (ParameterRegistrationImplementor) procedureCall.registerParameter( position, type, mode )
) )
); );
} }
@ -135,7 +141,7 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
registerParameter( registerParameter(
new ParameterRegistrationImpl( new ParameterRegistrationImpl(
this, this,
procedureCall.registerParameter( parameterName, type, mode ) (ParameterRegistrationImplementor) procedureCall.registerParameter( parameterName, type, mode )
) )
); );
} }
@ -453,15 +459,15 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
return procedureCall; return procedureCall;
} }
private static class ParameterRegistrationImpl<T> implements ParameterRegistration<T> { private static class ParameterRegistrationImpl<T> implements StoredProcedureQueryParameterRegistration<T> {
private final StoredProcedureQueryImpl query; private final StoredProcedureQueryImpl query;
private final org.hibernate.procedure.ParameterRegistration<T> nativeParamRegistration; private final ParameterRegistrationImplementor<T> nativeParamRegistration;
private ParameterBind<T> bind; private ParameterBind<T> bind;
public ParameterRegistrationImpl( public ParameterRegistrationImpl(
StoredProcedureQueryImpl query, StoredProcedureQueryImpl query,
org.hibernate.procedure.ParameterRegistration<T> nativeParamRegistration) { ParameterRegistrationImplementor<T> nativeParamRegistration) {
this.query = query; this.query = query;
this.nativeParamRegistration = nativeParamRegistration; this.nativeParamRegistration = nativeParamRegistration;
} }
@ -496,6 +502,16 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
return nativeParamRegistration.getMode(); return nativeParamRegistration.getMode();
} }
@Override
public boolean isPassNullsEnabled() {
return nativeParamRegistration.isPassNullsEnabled();
}
@Override
public void enablePassingNulls(boolean enabled) {
nativeParamRegistration.enablePassingNulls( enabled );
}
@Override @Override
public boolean isBindable() { public boolean isBindable() {
return getMode() == ParameterMode.IN || getMode() == ParameterMode.INOUT; return getMode() == ParameterMode.IN || getMode() == ParameterMode.INOUT;
@ -510,7 +526,7 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
public void bindValue(T value, TemporalType specifiedTemporalType) { public void bindValue(T value, TemporalType specifiedTemporalType) {
validateBinding( getParameterType(), value, specifiedTemporalType ); validateBinding( getParameterType(), value, specifiedTemporalType );
nativeParamRegistration.bindValue( value,specifiedTemporalType ); nativeParamRegistration.bindValue( value, specifiedTemporalType );
if ( bind == null ) { if ( bind == null ) {
bind = new ParameterBind<T>() { bind = new ParameterBind<T>() {

View File

@ -31,14 +31,14 @@ public interface ParameterRegistration<T> extends Parameter<T> {
* we have either a named parameter ({@link #getName()} would return a non-{@code null} value) or a native * we have either a named parameter ({@link #getName()} would return a non-{@code null} value) or a native
* Hibernate positional parameter. * Hibernate positional parameter.
*/ */
public boolean isJpaPositionalParameter(); boolean isJpaPositionalParameter();
/** /**
* Access to the query that this parameter belongs to. * Access to the query that this parameter belongs to.
* *
* @return The defining query * @return The defining query
*/ */
public Query getQuery(); Query getQuery();
/** /**
* Retrieves the parameter "mode" which describes how the parameter is defined in the actual database procedure * Retrieves the parameter "mode" which describes how the parameter is defined in the actual database procedure
@ -46,7 +46,7 @@ public interface ParameterRegistration<T> extends Parameter<T> {
* *
* @return The parameter mode. * @return The parameter mode.
*/ */
public ParameterMode getMode(); ParameterMode getMode();
/** /**
* Can we bind (set) values on this parameter? Generally this is {@code true}, but would not be in the case * Can we bind (set) values on this parameter? Generally this is {@code true}, but would not be in the case
@ -54,14 +54,14 @@ public interface ParameterRegistration<T> extends Parameter<T> {
* *
* @return Whether the parameter is bindable (can set be called). * @return Whether the parameter is bindable (can set be called).
*/ */
public boolean isBindable(); boolean isBindable();
/** /**
* If bindable, bind the value. * If bindable, bind the value.
* *
* @param value The value to bind. * @param value The value to bind.
*/ */
public void bindValue(T value); void bindValue(T value);
/** /**
* If bindable, bind the value using the specific temporal type. * If bindable, bind the value using the specific temporal type.
@ -69,12 +69,12 @@ public interface ParameterRegistration<T> extends Parameter<T> {
* @param value The value to bind * @param value The value to bind
* @param specifiedTemporalType The temporal type to use in binding * @param specifiedTemporalType The temporal type to use in binding
*/ */
public void bindValue(T value, TemporalType specifiedTemporalType); void bindValue(T value, TemporalType specifiedTemporalType);
/** /**
* If bindable, get the current binding. * If bindable, get the current binding.
* *
* @return The current binding * @return The current binding
*/ */
public ParameterBind<T> getBind(); ParameterBind<T> getBind();
} }

View File

@ -0,0 +1,38 @@
/*
* 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.jpa.spi;
import org.hibernate.procedure.spi.ParameterRegistrationImplementor;
/**
* ParameterRegistration extension specifically for stored procedure parameters
* exposing some functionality of Hibernate's native
* {@link org.hibernate.procedure.ParameterRegistration} contract
*
* @author Steve Ebersole
*/
public interface StoredProcedureQueryParameterRegistration<T> extends ParameterRegistration<T> {
/**
* How will an unbound value be handled in terms of the JDBC parameter?
*
* @return {@code true} here indicates that NULL should be passed; {@code false} indicates
* that it is ignored.
*
* @see ParameterRegistrationImplementor#isPassNullsEnabled()
*/
boolean isPassNullsEnabled();
/**
* Controls how unbound values for this IN/INOUT parameter registration will be handled prior to
* execution. For details see {@link org.hibernate.procedure.ParameterRegistration#enablePassingNulls}
*
* @param enabled {@code true} indicates that the NULL should be passed; {@code false} indicates it should not.
*
* @see org.hibernate.procedure.ParameterRegistration#enablePassingNulls
*/
void enablePassingNulls(boolean enabled);
}