HHH-12136 - Various improvements for ProcedureCall/StoredProcedureQuery

This commit is contained in:
Steve Ebersole 2017-11-29 12:33:00 -06:00
parent 15bf44e4e9
commit 6ba328e7a0
26 changed files with 1129 additions and 929 deletions

View File

@ -813,7 +813,7 @@ identPrimaryBase
;
castedIdentPrimaryBase
: i:IDENT! OPEN! p:path AS! a:path! CLOSE! { i.getText().equals("treat") }? {
: i:IDENT! OPEN! p:path AS! a:path! CLOSE! { i.getText().equalsIgnoreCase("treat") }? {
registerTreat( #p, #a );
}
;

View File

@ -25,6 +25,7 @@ import org.hibernate.internal.FilterImpl;
import org.hibernate.internal.util.EntityPrinter;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.query.internal.QueryParameterBindingsImpl;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type;
@ -230,7 +231,7 @@ public final class QueryParameters {
}
public QueryParameters(
QueryParameterBindingsImpl queryParameterBindings,
QueryParameterBindings queryParameterBindings,
LockOptions lockOptions,
RowSelection selection,
final boolean isReadOnlyInitialized,

View File

@ -8,10 +8,12 @@ package org.hibernate.procedure;
import javax.persistence.TemporalType;
import org.hibernate.query.spi.QueryParameterBinding;
/**
* Describes an input value binding for any IN/INOUT parameters.
*/
public interface ParameterBind<T> {
public interface ParameterBind<T> extends QueryParameterBinding<T> {
/**
* Retrieves the bound value.
*

View File

@ -9,6 +9,8 @@ package org.hibernate.procedure;
import javax.persistence.ParameterMode;
import javax.persistence.TemporalType;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.procedure.ProcedureParameter;
import org.hibernate.type.Type;
/**
@ -16,7 +18,7 @@ import org.hibernate.type.Type;
*
* @author Steve Ebersole
*/
public interface ParameterRegistration<T> {
public interface ParameterRegistration<T> extends ProcedureParameter<T> {
/**
* The name under which this parameter was registered. Can be {@code null} which should indicate that
* positional registration was used (and therefore {@link #getPosition()} should return non-null.
@ -33,14 +35,6 @@ public interface ParameterRegistration<T> {
*/
Integer getPosition();
/**
* Obtain the Java type of parameter. This is used to guess the Hibernate type (unless {@link #setHibernateType}
* is called explicitly).
*
* @return The parameter Java type.
*/
Class<T> getType();
/**
* Retrieves the parameter "mode" which describes how the parameter is defined in the actual database procedure
* definition (is it an INPUT parameter? An OUTPUT parameter? etc).

View File

@ -1,431 +0,0 @@
/*
* 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.procedure.internal;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Calendar;
import java.util.Date;
import javax.persistence.ParameterMode;
import javax.persistence.TemporalType;
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.procedure.ParameterBind;
import org.hibernate.procedure.ParameterMisuseException;
import org.hibernate.procedure.spi.ParameterRegistrationImplementor;
import org.hibernate.procedure.spi.ParameterStrategy;
import org.hibernate.type.CalendarDateType;
import org.hibernate.type.CalendarTimeType;
import org.hibernate.type.CalendarType;
import org.hibernate.type.ProcedureParameterExtractionAware;
import org.hibernate.type.ProcedureParameterNamedBinder;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
/**
* Abstract implementation of ParameterRegistration/ParameterRegistrationImplementor
*
* @author Steve Ebersole
*/
public abstract class AbstractParameterRegistrationImpl<T> implements ParameterRegistrationImplementor<T> {
private static final Logger log = Logger.getLogger( AbstractParameterRegistrationImpl.class );
private final ProcedureCallImpl procedureCall;
private final Integer position;
private final String name;
private final ParameterMode mode;
private final Class<T> type;
private ParameterBindImpl bind;
private boolean passNulls;
private int startIndex;
private Type hibernateType;
private int[] sqlTypes;
// positional constructors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
protected AbstractParameterRegistrationImpl(
ProcedureCallImpl procedureCall,
Integer position,
ParameterMode mode,
Class<T> type,
boolean initialPassNullsSetting) {
this( procedureCall, position, null, mode, type, initialPassNullsSetting );
}
protected AbstractParameterRegistrationImpl(
ProcedureCallImpl procedureCall,
Integer position,
ParameterMode mode,
Class<T> type,
Type hibernateType,
boolean initialPassNullsSetting) {
this( procedureCall, position, null, mode, type, hibernateType, initialPassNullsSetting );
}
// named constructors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
protected AbstractParameterRegistrationImpl(
ProcedureCallImpl procedureCall,
String name,
ParameterMode mode,
Class<T> type,
boolean initialPassNullsSetting) {
this( procedureCall, null, name, mode, type, initialPassNullsSetting );
}
protected AbstractParameterRegistrationImpl(
ProcedureCallImpl procedureCall,
String name,
ParameterMode mode,
Class<T> type,
Type hibernateType,
boolean initialPassNullsSetting) {
this( procedureCall, null, name, mode, type, hibernateType, initialPassNullsSetting );
}
// full constructors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private AbstractParameterRegistrationImpl(
ProcedureCallImpl procedureCall,
Integer position,
String name,
ParameterMode mode,
Class<T> type,
Type hibernateType,
boolean initialPassNullsSetting) {
this.procedureCall = procedureCall;
this.position = position;
this.name = name;
this.mode = mode;
this.type = type;
if ( mode == ParameterMode.REF_CURSOR ) {
this.sqlTypes = new int[]{ Types.REF_CURSOR };
}
else {
this.passNulls = initialPassNullsSetting;
setHibernateType( hibernateType );
}
}
private AbstractParameterRegistrationImpl(
ProcedureCallImpl procedureCall,
Integer position,
String name,
ParameterMode mode,
Class<T> type,
boolean initialPassNullsSetting) {
this(
procedureCall,
position,
name,
mode,
type,
procedureCall.getSession().getFactory().getTypeResolver().heuristicType( type.getName() ),
initialPassNullsSetting
);
}
protected SharedSessionContractImplementor session() {
return procedureCall.getSession();
}
@Override
public String getName() {
return name;
}
@Override
public Integer getPosition() {
return position;
}
@Override
public Class<T> getType() {
return type;
}
@Override
public ParameterMode getMode() {
return mode;
}
@Override
public boolean isPassNullsEnabled() {
return passNulls;
}
@Override
public void enablePassingNulls(boolean enabled) {
this.passNulls = enabled;
}
@Override
public Type getHibernateType() {
return hibernateType;
}
@Override
public void setHibernateType(Type type) {
if ( type == null ) {
throw new IllegalArgumentException( "Type cannot be null" );
}
this.hibernateType = type;
this.sqlTypes = hibernateType.sqlTypes( session().getFactory() );
}
@Override
@SuppressWarnings("unchecked")
public ParameterBind<T> getBind() {
return bind;
}
@Override
public void bindValue(T value) {
validateBindability();
this.bind = new ParameterBindImpl<T>( value );
}
private void validateBindability() {
if ( ! canBind() ) {
throw new ParameterMisuseException( "Cannot bind value to non-input parameter : " + this );
}
}
private boolean canBind() {
return mode == ParameterMode.IN || mode == ParameterMode.INOUT;
}
@Override
public void bindValue(T value, TemporalType explicitTemporalType) {
validateBindability();
if ( explicitTemporalType != null ) {
if ( ! isDateTimeType() ) {
throw new IllegalArgumentException( "TemporalType should not be specified for non date/time type" );
}
}
this.bind = new ParameterBindImpl<T>( value, explicitTemporalType );
}
private boolean isDateTimeType() {
return Date.class.isAssignableFrom( type )
|| Calendar.class.isAssignableFrom( type );
}
@Override
public void prepare(CallableStatement statement, int startIndex) throws SQLException {
// initially set up the Type we will use for binding as the explicit type.
Type typeToUse = hibernateType;
int[] sqlTypesToUse = sqlTypes;
// however, for Calendar binding with an explicit TemporalType we may need to adjust this...
if ( bind != null && bind.getExplicitTemporalType() != null ) {
if ( Calendar.class.isInstance( bind.getValue() ) ) {
switch ( bind.getExplicitTemporalType() ) {
case TIMESTAMP: {
typeToUse = CalendarType.INSTANCE;
sqlTypesToUse = typeToUse.sqlTypes( session().getFactory() );
break;
}
case DATE: {
typeToUse = CalendarDateType.INSTANCE;
sqlTypesToUse = typeToUse.sqlTypes( session().getFactory() );
break;
}
case TIME: {
typeToUse = CalendarTimeType.INSTANCE;
sqlTypesToUse = typeToUse.sqlTypes( session().getFactory() );
break;
}
}
}
}
this.startIndex = startIndex;
if ( mode == ParameterMode.IN || mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) {
if ( mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) {
if ( sqlTypesToUse.length > 1 ) {
// there is more than one column involved; see if the Hibernate Type can handle
// multi-param extraction...
final boolean canHandleMultiParamExtraction =
ProcedureParameterExtractionAware.class.isInstance( hibernateType )
&& ( (ProcedureParameterExtractionAware) hibernateType ).canDoExtraction();
if ( ! canHandleMultiParamExtraction ) {
// it cannot...
throw new UnsupportedOperationException(
"Type [" + hibernateType + "] does support multi-parameter value extraction"
);
}
}
// TODO: sqlTypesToUse.length > 1 does not seem to have a working use case (HHH-10769).
// The idea is that an embeddable/custom type can have more than one column values
// that correspond with embeddable/custom attribute value. This does not seem to
// be working yet. For now, if sqlTypesToUse.length > 1, then register
// the out parameters by position (since we only have one name).
// This will cause a failure if there are other parameters bound by
// name and the dialect does not support "mixed" named/positional parameters;
// e.g., Oracle.
if ( sqlTypesToUse.length == 1 &&
procedureCall.getParameterStrategy() == ParameterStrategy.NAMED &&
canDoNameParameterBinding() ) {
statement.registerOutParameter( getName(), sqlTypesToUse[0] );
}
else {
for ( int i = 0; i < sqlTypesToUse.length; i++ ) {
statement.registerOutParameter( startIndex + i, sqlTypesToUse[i] );
}
}
}
if ( mode == ParameterMode.INOUT || mode == ParameterMode.IN ) {
if ( bind == null || bind.getValue() == null ) {
// the user did not bind a value to the parameter being processed. This is the condition
// defined by `passNulls` and that value controls what happens here. If `passNulls` is
// {@code true} we will bind the NULL value into the statement; if `passNulls` is
// {@code false} we will not.
//
// Unfortunately there is not a way to reliably know through JDBC metadata whether a procedure
// parameter defines a default value. Deferring to that information would be the best option
if ( passNulls ) {
log.debugf(
"Stored procedure [%s] IN/INOUT parameter [%s] not bound and `passNulls` was set to true; binding NULL",
procedureCall.getProcedureName(),
this
);
if ( this.procedureCall.getParameterStrategy() == ParameterStrategy.NAMED && canDoNameParameterBinding() ) {
((ProcedureParameterNamedBinder) typeToUse).nullSafeSet(
statement,
null,
this.getName(),
session()
);
}
else {
typeToUse.nullSafeSet( statement, null, startIndex, session() );
}
}
else {
log.debugf(
"Stored procedure [%s] IN/INOUT parameter [%s] not bound and `passNulls` was set to false; assuming procedure defines default value",
procedureCall.getProcedureName(),
this
);
}
}
else {
if ( this.procedureCall.getParameterStrategy() == ParameterStrategy.NAMED && canDoNameParameterBinding()) {
((ProcedureParameterNamedBinder) typeToUse).nullSafeSet(
statement,
bind.getValue(),
this.getName(),
session()
);
}
else {
typeToUse.nullSafeSet( statement, bind.getValue(), startIndex, session() );
}
}
}
}
else {
// we have a REF_CURSOR type param
if ( procedureCall.getParameterStrategy() == ParameterStrategy.NAMED ) {
session().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class )
.registerRefCursorParameter( statement, getName() );
}
else {
session().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class )
.registerRefCursorParameter( statement, startIndex );
}
}
}
private boolean canDoNameParameterBinding() {
final ExtractedDatabaseMetaData databaseMetaData = session()
.getJdbcCoordinator()
.getJdbcSessionOwner()
.getJdbcSessionContext()
.getServiceRegistry().getService( JdbcEnvironment.class )
.getExtractedDatabaseMetaData();
return
databaseMetaData.supportsNamedParameters() &&
ProcedureParameterNamedBinder.class.isInstance( hibernateType )
&& ((ProcedureParameterNamedBinder) hibernateType).canDoSetting();
}
public int[] getSqlTypes() {
if ( mode == ParameterMode.REF_CURSOR ) {
// we could use the Types#REF_CURSOR added in Java 8, but that would require requiring Java 8...
throw new IllegalStateException( "REF_CURSOR parameters do not have a SQL/JDBC type" );
}
return sqlTypes;
}
@Override
@SuppressWarnings("unchecked")
public T extract(CallableStatement statement) {
if ( mode == ParameterMode.IN ) {
throw new ParameterMisuseException( "IN parameter not valid for output extraction" );
}
// TODO: sqlTypesToUse.length > 1 does not seem to have a working use case (HHH-10769).
// For now, if sqlTypes.length > 1 with a named parameter, then extract
// parameter values by position (since we only have one name).
final boolean useNamed = sqlTypes.length == 1 &&
procedureCall.getParameterStrategy() == ParameterStrategy.NAMED &&
canDoNameParameterBinding();
try {
if ( ProcedureParameterExtractionAware.class.isInstance( hibernateType ) ) {
if ( useNamed ) {
return (T) ( (ProcedureParameterExtractionAware) hibernateType ).extract(
statement,
new String[] { getName() },
session()
);
}
else {
return (T) ( (ProcedureParameterExtractionAware) hibernateType ).extract(
statement,
startIndex,
session()
);
}
}
else {
if ( useNamed ) {
return (T) statement.getObject( name );
}
else {
return (T) statement.getObject( startIndex );
}
}
}
catch (SQLException e) {
throw procedureCall.getSession().getFactory().getSQLExceptionHelper().convert(
e,
"Unable to extract OUT/INOUT parameter value"
);
}
}
}

View File

@ -1,37 +0,0 @@
/*
* 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.procedure.internal;
import javax.persistence.ParameterMode;
import org.hibernate.type.Type;
/**
* Represents a registered named parameter
*
* @author Steve Ebersole
*/
public class NamedParameterRegistration<T> extends AbstractParameterRegistrationImpl<T> {
NamedParameterRegistration(
ProcedureCallImpl procedureCall,
String name,
ParameterMode mode,
Class<T> type,
boolean initialPassNullsSetting) {
super( procedureCall, name, mode, type, initialPassNullsSetting );
}
NamedParameterRegistration(
ProcedureCallImpl procedureCall,
String name,
ParameterMode mode,
Class<T> type,
Type hibernateType,
boolean initialPassNullsSetting) {
super( procedureCall, name, mode, type, hibernateType, initialPassNullsSetting );
}
}

View File

@ -6,9 +6,16 @@
*/
package org.hibernate.procedure.internal;
import javax.persistence.ParameterMode;
import javax.persistence.TemporalType;
import org.hibernate.procedure.ParameterBind;
import org.hibernate.query.internal.BindingTypeHelper;
import org.hibernate.query.procedure.internal.ProcedureParamBindings;
import org.hibernate.query.procedure.spi.ProcedureParameterImplementor;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
/**
* Implementation of the {@link ParameterBind} contract.
@ -16,16 +23,25 @@ import org.hibernate.procedure.ParameterBind;
* @author Steve Ebersole
*/
public class ParameterBindImpl<T> implements ParameterBind<T> {
private final T value;
private final TemporalType explicitTemporalType;
private static final Logger log = Logger.getLogger( ParameterBindImpl.class );
ParameterBindImpl(T value) {
this( value, null );
}
private final ProcedureParameterImplementor procedureParameter;
private final ProcedureParamBindings procedureParamBindings;
ParameterBindImpl(T value, TemporalType explicitTemporalType) {
this.value = value;
this.explicitTemporalType = explicitTemporalType;
private boolean isBound;
private T value;
private Type hibernateType;
private TemporalType explicitTemporalType;
public ParameterBindImpl(
ProcedureParameterImplementor procedureParameter,
ProcedureParamBindings procedureParamBindings) {
this.procedureParameter = procedureParameter;
this.procedureParamBindings = procedureParamBindings;
this.hibernateType = procedureParameter.getHibernateType();
}
@Override
@ -37,4 +53,66 @@ public class ParameterBindImpl<T> implements ParameterBind<T> {
public TemporalType getExplicitTemporalType() {
return explicitTemporalType;
}
@Override
public boolean isBound() {
return isBound;
}
@Override
public void setBindValue(T value) {
internalSetValue( value );
if ( value != null && hibernateType == null ) {
hibernateType = procedureParamBindings.getProcedureCall()
.getSession()
.getFactory()
.getTypeResolver()
.heuristicType( value.getClass().getName() );
log.debugf( "Using heuristic type [%s] based on bind value [%s] as `bindType`", hibernateType, value );
}
}
private void internalSetValue(T value) {
if ( procedureParameter.getMode() != ParameterMode.IN && procedureParameter.getMode() != ParameterMode.INOUT ) {
throw new IllegalStateException( "Can only bind values for IN/INOUT parameters : " + procedureParameter );
}
if ( procedureParameter.getParameterType() != null ) {
if ( !procedureParameter.getParameterType().isInstance( value ) ) {
throw new IllegalArgumentException( "Bind value [" + value + "] was not of specified type [" + procedureParameter.getParameterType() );
}
}
this.value = value;
this.isBound = true;
}
@Override
public void setBindValue(T value, Type clarifiedType) {
internalSetValue( value );
this.hibernateType = clarifiedType;
log.debugf( "Using explicit type [%s] as `bindType`", hibernateType, value );
}
@Override
public void setBindValue(T value, TemporalType clarifiedTemporalType) {
internalSetValue( value );
this.hibernateType = BindingTypeHelper.INSTANCE.determineTypeForTemporalType( clarifiedTemporalType, hibernateType, value );
this.explicitTemporalType = clarifiedTemporalType;
log.debugf( "Using type [%s] (based on TemporalType [%s] as `bindType`", hibernateType, clarifiedTemporalType );
}
@Override
public T getBindValue() {
if ( !isBound ) {
throw new IllegalStateException( "Value not yet bound" );
}
return value;
}
@Override
public Type getBindType() {
return hibernateType;
}
}

View File

@ -1,37 +0,0 @@
/*
* 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.procedure.internal;
import javax.persistence.ParameterMode;
import org.hibernate.type.Type;
/**
* Represents a registered positional parameter
*
* @author Steve Ebersole
*/
public class PositionalParameterRegistration<T> extends AbstractParameterRegistrationImpl<T> {
PositionalParameterRegistration(
ProcedureCallImpl procedureCall,
Integer position,
ParameterMode mode,
Class<T> type,
boolean initialPassNullsSetting) {
super( procedureCall, position, mode, type, initialPassNullsSetting );
}
PositionalParameterRegistration(
ProcedureCallImpl procedureCall,
Integer position,
ParameterMode mode,
Class<T> type,
Type hibernateType,
boolean initialPassNullsSetting) {
super( procedureCall, position, mode, type, hibernateType, initialPassNullsSetting );
}
}

View File

@ -11,13 +11,13 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.NoResultException;
@ -28,17 +28,13 @@ import javax.persistence.TemporalType;
import javax.persistence.TransactionRequiredException;
import org.hibernate.HibernateException;
import org.hibernate.QueryException;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.procedure.NoSuchParameterException;
import org.hibernate.procedure.ParameterRegistration;
@ -51,8 +47,12 @@ import org.hibernate.procedure.spi.ParameterStrategy;
import org.hibernate.procedure.spi.ProcedureCallImplementor;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.internal.AbstractProducedQuery;
import org.hibernate.query.procedure.internal.ProcedureParamBindings;
import org.hibernate.query.procedure.internal.ProcedureParameterImpl;
import org.hibernate.query.procedure.internal.ProcedureParameterMetadata;
import org.hibernate.query.procedure.spi.ProcedureParameterImplementor;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.result.NoMoreReturnsException;
import org.hibernate.result.Output;
import org.hibernate.result.ResultSetOutput;
@ -82,8 +82,8 @@ public class ProcedureCallImpl<R>
private final boolean globalParameterPassNullsSetting;
private ParameterStrategy parameterStrategy = ParameterStrategy.UNKNOWN;
private List<ParameterRegistrationImplementor<?>> registeredParameters = new ArrayList<>();
private final ProcedureParameterMetadata parameterMetadata;
private final ProcedureParamBindings paramBindings;
private Set<String> synchronizedQuerySpaces;
@ -96,11 +96,14 @@ public class ProcedureCallImpl<R>
* @param procedureName The name of the procedure to call
*/
public ProcedureCallImpl(SharedSessionContractImplementor session, String procedureName) {
super( session, new ProcedureParameterMetadata() );
super( session, null );
this.procedureName = procedureName;
this.globalParameterPassNullsSetting = session.getFactory().getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
this.queryReturns = NO_RETURNS;
this.parameterMetadata = new ProcedureParameterMetadata( this );
this.paramBindings = new ProcedureParamBindings( parameterMetadata, this );
}
/**
@ -111,7 +114,7 @@ public class ProcedureCallImpl<R>
* @param resultClasses The classes making up the result
*/
public ProcedureCallImpl(final SharedSessionContractImplementor session, String procedureName, Class... resultClasses) {
super( session, new ProcedureParameterMetadata() );
super( session, null );
this.procedureName = procedureName;
this.globalParameterPassNullsSetting = session.getFactory().getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
@ -140,6 +143,9 @@ public class ProcedureCallImpl<R>
this.queryReturns = collectedQueryReturns.toArray( new NativeSQLQueryReturn[ collectedQueryReturns.size() ] );
this.synchronizedQuerySpaces = collectedQuerySpaces;
this.parameterMetadata = new ProcedureParameterMetadata( this );
this.paramBindings = new ProcedureParamBindings( parameterMetadata, this );
}
/**
@ -150,7 +156,7 @@ public class ProcedureCallImpl<R>
* @param resultSetMappings The names of the result set mappings making up the result
*/
public ProcedureCallImpl(final SharedSessionContractImplementor session, String procedureName, String... resultSetMappings) {
super( session, new ProcedureParameterMetadata() );
super( session, null );
this.procedureName = procedureName;
this.globalParameterPassNullsSetting = session.getFactory().getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
@ -184,6 +190,9 @@ public class ProcedureCallImpl<R>
this.queryReturns = collectedQueryReturns.toArray( new NativeSQLQueryReturn[ collectedQueryReturns.size() ] );
this.synchronizedQuerySpaces = collectedQuerySpaces;
this.parameterMetadata = new ProcedureParameterMetadata( this );
this.paramBindings = new ProcedureParamBindings( parameterMetadata, this );
}
/**
@ -194,41 +203,21 @@ public class ProcedureCallImpl<R>
*/
@SuppressWarnings("unchecked")
ProcedureCallImpl(SharedSessionContractImplementor session, ProcedureCallMementoImpl memento) {
super( session, new ProcedureParameterMetadata() );
super( session, null );
this.procedureName = memento.getProcedureName();
this.globalParameterPassNullsSetting = session.getFactory().getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
this.queryReturns = memento.getQueryReturns();
this.synchronizedQuerySpaces = Util.copy( memento.getSynchronizedQuerySpaces() );
this.parameterStrategy = memento.getParameterStrategy();
if ( parameterStrategy == ParameterStrategy.UNKNOWN ) {
// nothing else to do in this case
return;
}
final List<ProcedureCallMementoImpl.ParameterMemento> storedRegistrations = memento.getParameterDeclarations();
if ( storedRegistrations == null ) {
// most likely a problem if ParameterStrategy is not UNKNOWN...
LOG.debugf(
"ParameterStrategy was [%s] on named copy [%s], but no parameters stored",
parameterStrategy,
procedureName
);
return;
}
this.parameterMetadata = new ProcedureParameterMetadata( this );
this.paramBindings = new ProcedureParamBindings( parameterMetadata, this );
final List<ParameterRegistrationImplementor<?>> parameterRegistrations =
CollectionHelper.arrayList( storedRegistrations.size() );
for ( ProcedureCallMementoImpl.ParameterMemento storedRegistration : memento.getParameterDeclarations() ) {
final ProcedureParameterImplementor<?> registration;
for ( ProcedureCallMementoImpl.ParameterMemento storedRegistration : storedRegistrations ) {
final ParameterRegistrationImplementor<?> registration;
if ( StringHelper.isNotEmpty( storedRegistration.getName() ) ) {
if ( parameterStrategy != ParameterStrategy.NAMED ) {
throw new IllegalStateException(
"Found named stored procedure parameter associated with positional parameters"
);
}
registration = new NamedParameterRegistration(
registration = new ProcedureParameterImpl(
this,
storedRegistration.getName(),
storedRegistration.getMode(),
@ -238,12 +227,7 @@ public class ProcedureCallImpl<R>
);
}
else {
if ( parameterStrategy != ParameterStrategy.POSITIONAL ) {
throw new IllegalStateException(
"Found named stored procedure parameter associated with positional parameters"
);
}
registration = new PositionalParameterRegistration(
registration = new ProcedureParameterImpl(
this,
storedRegistration.getPosition(),
storedRegistration.getMode(),
@ -252,10 +236,9 @@ public class ProcedureCallImpl<R>
storedRegistration.isPassNullsEnabled()
);
}
getParameterMetadata().registerParameter( new ProcedureParameterImpl( registration ) );
parameterRegistrations.add( registration );
getParameterMetadata().registerParameter( registration );
}
this.registeredParameters = parameterRegistrations;
for ( Map.Entry<String, Object> entry : memento.getHintsMap().entrySet() ) {
setHint( entry.getKey(), entry.getValue() );
@ -264,7 +247,12 @@ public class ProcedureCallImpl<R>
@Override
public ProcedureParameterMetadata getParameterMetadata() {
return (ProcedureParameterMetadata) super.getParameterMetadata();
return parameterMetadata;
}
@Override
public QueryParameterBindings getQueryParameterBindings() {
return paramBindings;
}
@Override
@ -273,7 +261,7 @@ public class ProcedureCallImpl<R>
}
public ParameterStrategy getParameterStrategy() {
return parameterStrategy;
return getParameterMetadata().getParameterStrategy();
}
@Override
@ -294,11 +282,17 @@ public class ProcedureCallImpl<R>
@Override
@SuppressWarnings("unchecked")
public <T> ParameterRegistration<T> registerParameter(int position, Class<T> type, ParameterMode mode) {
final ProcedureParameterImpl procedureParameter = new ProcedureParameterImpl(
this,
position,
mode,
type,
getSession().getFactory().getTypeResolver().heuristicType( type.getName() ),
globalParameterPassNullsSetting
);
final PositionalParameterRegistration parameterRegistration =
new PositionalParameterRegistration( this, position, mode, type, globalParameterPassNullsSetting );
registerParameter( parameterRegistration );
return parameterRegistration;
registerParameter( procedureParameter );
return procedureParameter;
}
@Override
@ -308,68 +302,30 @@ public class ProcedureCallImpl<R>
return this;
}
private void registerParameter(ParameterRegistrationImplementor parameter) {
if ( StringHelper.isNotEmpty( parameter.getName() ) ) {
prepareForNamedParameters();
}
else if ( parameter.getPosition() != null ) {
prepareForPositionalParameters();
}
else {
throw new IllegalArgumentException( "Given parameter did not define name or position [" + parameter + "]" );
}
((ProcedureParameterMetadata)getParameterMetadata()).registerParameter( new ProcedureParameterImpl( parameter ) );
registeredParameters.add( parameter );
}
private void prepareForPositionalParameters() {
if ( parameterStrategy == ParameterStrategy.NAMED ) {
throw new QueryException( "Cannot mix named and positional parameters" );
}
parameterStrategy = ParameterStrategy.POSITIONAL;
}
private void prepareForNamedParameters() {
if ( parameterStrategy == ParameterStrategy.POSITIONAL ) {
throw new QueryException( "Cannot mix named and positional parameters" );
}
if ( parameterStrategy == ParameterStrategy.UNKNOWN ) {
// protect to only do this check once
final ExtractedDatabaseMetaData databaseMetaData = getSession()
.getJdbcCoordinator()
.getJdbcSessionOwner()
.getJdbcSessionContext()
.getServiceRegistry().getService( JdbcEnvironment.class )
.getExtractedDatabaseMetaData();
if ( ! databaseMetaData.supportsNamedParameters() ) {
LOG.unsupportedNamedParameters();
}
parameterStrategy = ParameterStrategy.NAMED;
}
private void registerParameter(ProcedureParameterImplementor parameter) {
getParameterMetadata().registerParameter( parameter );
}
@Override
public ParameterRegistrationImplementor getParameterRegistration(int position) {
if ( parameterStrategy != ParameterStrategy.POSITIONAL ) {
throw new ParameterStrategyException(
"Attempt to access positional parameter [" + position + "] but ProcedureCall using named parameters"
);
}
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
if ( position == parameter.getPosition() ) {
return parameter;
}
}
throw new NoSuchParameterException( "Could not locate parameter registered using that position [" + position + "]" );
return getParameterMetadata().getQueryParameter( position );
}
@Override
@SuppressWarnings("unchecked")
public <T> ParameterRegistration<T> registerParameter(String name, Class<T> type, ParameterMode mode) {
final NamedParameterRegistration parameterRegistration = new NamedParameterRegistration( this, name, mode, type, globalParameterPassNullsSetting );
registerParameter( parameterRegistration );
return parameterRegistration;
final ProcedureParameterImpl parameter = new ProcedureParameterImpl(
this,
name,
mode,
type,
getSession().getFactory().getTypeResolver().heuristicType( type.getName() ),
globalParameterPassNullsSetting
);
registerParameter( parameter );
return parameter;
}
@Override
@ -381,21 +337,13 @@ public class ProcedureCallImpl<R>
@Override
public ParameterRegistrationImplementor getParameterRegistration(String name) {
if ( parameterStrategy != ParameterStrategy.NAMED ) {
throw new ParameterStrategyException( "Names were not used to register parameters with this stored procedure call" );
}
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
if ( name.equals( parameter.getName() ) ) {
return parameter;
}
}
throw new NoSuchParameterException( "Could not locate parameter registered under that name [" + name + "]" );
return getParameterMetadata().getQueryParameter( name );
}
@Override
@SuppressWarnings("unchecked")
public List<ParameterRegistration> getRegisteredParameters() {
return new ArrayList<>( registeredParameters );
public List getRegisteredParameters() {
return new ArrayList( getParameterMetadata().collectAllParameters() );
}
@Override
@ -424,12 +372,11 @@ public class ProcedureCallImpl<R>
final String call = getProducer().getJdbcServices().getJdbcEnvironment().getDialect().getCallableStatementSupport().renderCallableStatement(
procedureName,
parameterStrategy,
registeredParameters,
getParameterMetadata(),
paramBindings,
getProducer()
);
try {
LOG.debugf( "Preparing procedure call : %s", call );
final CallableStatement statement = (CallableStatement) getSession()
.getJdbcCoordinator()
@ -438,28 +385,36 @@ public class ProcedureCallImpl<R>
// prepare parameters
getParameterMetadata().visitRegistrations(
new Consumer<QueryParameter>() {
int i = 1;
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
parameter.prepare( statement, i );
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
@Override
public void accept(QueryParameter queryParameter) {
try {
final ParameterRegistrationImplementor registration = (ParameterRegistrationImplementor) queryParameter;
registration.prepare( statement, i );
if ( registration.getMode() == ParameterMode.REF_CURSOR ) {
i++;
}
else {
i += parameter.getSqlTypes().length;
i += registration.getSqlTypes().length;
}
}
return new ProcedureOutputsImpl( this, statement );
}
catch (SQLException e) {
throw getSession().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Error preparing CallableStatement",
"Error preparing registered callable parameter",
getProcedureName()
);
}
}
}
);
return new ProcedureOutputsImpl( this, statement );
}
@Override
public String getQueryString() {
@ -492,6 +447,7 @@ public class ProcedureCallImpl<R>
*
* @return The spaces
*/
@SuppressWarnings("WeakerAccess")
protected Set<String> synchronizedQuerySpaces() {
if ( synchronizedQuerySpaces == null ) {
synchronizedQuerySpaces = new HashSet<>();
@ -522,6 +478,7 @@ public class ProcedureCallImpl<R>
return this;
}
@SuppressWarnings("WeakerAccess")
protected void addSynchronizedQuerySpaces(EntityPersister persister) {
synchronizedQuerySpaces().addAll( Arrays.asList( (String[]) persister.getQuerySpaces() ) );
}
@ -553,11 +510,15 @@ public class ProcedureCallImpl<R>
*/
public ParameterRegistrationImplementor[] collectRefCursorParameters() {
final List<ParameterRegistrationImplementor> refCursorParams = new ArrayList<>();
for ( ParameterRegistrationImplementor param : registeredParameters ) {
if ( param.getMode() == ParameterMode.REF_CURSOR ) {
refCursorParams.add( param );
getParameterMetadata().visitRegistrations(
queryParameter -> {
final ParameterRegistrationImplementor registration = (ParameterRegistrationImplementor) queryParameter;
if ( registration.getMode() == ParameterMode.REF_CURSOR ) {
refCursorParams.add( registration );
}
}
);
return refCursorParams.toArray( new ParameterRegistrationImplementor[refCursorParams.size()] );
}
@ -566,8 +527,8 @@ public class ProcedureCallImpl<R>
return new ProcedureCallMementoImpl(
procedureName,
Util.copy( queryReturns ),
parameterStrategy,
toParameterMementos( registeredParameters ),
getParameterMetadata().getParameterStrategy(),
toParameterMementos( getParameterMetadata() ),
Util.copy( synchronizedQuerySpaces ),
Util.copy( hints )
);
@ -578,22 +539,28 @@ public class ProcedureCallImpl<R>
return new ProcedureCallMementoImpl(
procedureName,
Util.copy( queryReturns ),
parameterStrategy,
toParameterMementos( registeredParameters ),
getParameterMetadata().getParameterStrategy(),
toParameterMementos( getParameterMetadata() ),
Util.copy( synchronizedQuerySpaces ),
Util.copy( getHints() )
);
}
private static List<ProcedureCallMementoImpl.ParameterMemento> toParameterMementos(List<ParameterRegistrationImplementor<?>> registeredParameters) {
if ( registeredParameters == null ) {
return null;
private static List<ProcedureCallMementoImpl.ParameterMemento> toParameterMementos(ProcedureParameterMetadata parameterMetadata) {
if ( parameterMetadata.getParameterStrategy() == ParameterStrategy.UNKNOWN ) {
// none...
return Collections.emptyList();
}
final List<ProcedureCallMementoImpl.ParameterMemento> copy = CollectionHelper.arrayList( registeredParameters.size() );
for ( ParameterRegistrationImplementor registration : registeredParameters ) {
final List<ProcedureCallMementoImpl.ParameterMemento> copy = new ArrayList<>();
parameterMetadata.visitRegistrations(
queryParameter -> {
final ParameterRegistrationImplementor registration = (ParameterRegistrationImplementor) queryParameter;
copy.add( ProcedureCallMementoImpl.ParameterMemento.fromRegistration( registration ) );
}
);
return copy;
}
@ -604,6 +571,7 @@ public class ProcedureCallImpl<R>
private ProcedureOutputs procedureResult;
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> registerStoredProcedureParameter(int position, Class type, ParameterMode mode) {
getProducer().checkOpen( true );
@ -622,6 +590,7 @@ public class ProcedureCallImpl<R>
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> registerStoredProcedureParameter(String parameterName, Class type, ParameterMode mode) {
getProducer().checkOpen( true );
try {
@ -828,182 +797,127 @@ public class ProcedureCallImpl<R>
return this;
}
// todo (5.3) : all of the parameter stuff here can be done in AbstractProducedQuery
// using #getParameterMetadata and #getQueryParameterBindings for abstraction.
// this "win" is to define these in one place
@Override
public <P> ProcedureCallImplementor<R> setParameter(QueryParameter<P> parameter, P value) {
locateParameterRegistration( parameter ).bindValue( value );
paramBindings.getBinding( getParameterMetadata().resolve( parameter ) ).setBindValue( value );
return this;
}
@SuppressWarnings("unchecked")
private <P> ParameterRegistrationImplementor<P> locateParameterRegistration(Parameter<P> parameter) {
if ( parameter.getName() != null ) {
return locateParameterRegistration( parameter.getName() );
}
if ( parameter.getPosition() != null ) {
return locateParameterRegistration( parameter.getPosition() );
}
throw getExceptionConverter().convert(
new IllegalArgumentException( "Could not resolve registration for given parameter reference [" + parameter + "]" )
);
}
@SuppressWarnings("unchecked")
private <P> ParameterRegistrationImplementor<P> locateParameterRegistration(String name) {
assert name != null;
if ( parameterStrategy == ParameterStrategy.POSITIONAL ) {
throw new IllegalArgumentException( "Expecting positional parameter" );
}
for ( ParameterRegistrationImplementor<?> registeredParameter : registeredParameters ) {
if ( name.equals( registeredParameter.getName() ) ) {
return (ParameterRegistrationImplementor<P>) registeredParameter;
}
}
throw new IllegalArgumentException( "Unknown parameter registration name [" + name + "]" );
}
@SuppressWarnings("unchecked")
private <P> ParameterRegistrationImplementor<P> locateParameterRegistration(int position) {
if ( parameterStrategy == ParameterStrategy.NAMED ) {
throw new IllegalArgumentException( "Expecting named parameter" );
}
for ( ParameterRegistrationImplementor<?> registeredParameter : registeredParameters ) {
if ( registeredParameter.getPosition() != null && registeredParameter.getPosition() == position ) {
return (ParameterRegistrationImplementor<P>) registeredParameter;
}
}
throw new IllegalArgumentException( "Unknown parameter registration position [" + position + "]" );
}
@Override
public <P> ProcedureCallImplementor<R> setParameter(Parameter<P> parameter, P value) {
locateParameterRegistration( parameter ).bindValue( value );
paramBindings.getBinding( getParameterMetadata().resolve( parameter ) ).setBindValue( value );
return this;
}
@Override
public ProcedureCallImplementor<R> setParameter(String name, Object value) {
locateParameterRegistration( name ).bindValue( value );
paramBindings.getBinding( getParameterMetadata().getQueryParameter( name ) ).setBindValue( value );
return this;
}
@Override
public ProcedureCallImplementor<R> setParameter(int position, Object value) {
locateParameterRegistration( position ).bindValue( value );
paramBindings.getBinding( getParameterMetadata().getQueryParameter( position ) ).setBindValue( value );
return this;
}
@Override
public <P> ProcedureCallImplementor<R> setParameter(QueryParameter<P> parameter, P value, Type type) {
final ParameterRegistrationImplementor<P> reg = locateParameterRegistration( parameter );
reg.bindValue( value );
reg.setHibernateType( type );
final QueryParameterBinding<P> binding = paramBindings.getBinding( parameter );
binding.setBindValue( value, type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> setParameter(String name, Object value, Type type) {
final ParameterRegistrationImplementor reg = locateParameterRegistration( name );
reg.bindValue( value );
reg.setHibernateType( type );
final QueryParameterBinding binding = paramBindings.getBinding( getParameterMetadata().getQueryParameter( name ) );
binding.setBindValue( value, type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> setParameter(int position, Object value, Type type) {
final ParameterRegistrationImplementor reg = locateParameterRegistration( position );
reg.bindValue( value );
reg.setHibernateType( type );
final QueryParameterBinding binding = paramBindings.getBinding( getParameterMetadata().getQueryParameter( position ) );
binding.setBindValue( value, type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public <P> ProcedureCallImplementor<R> setParameter(QueryParameter<P> parameter, P value, TemporalType temporalType) {
locateParameterRegistration( parameter ).bindValue( value, temporalType );
final QueryParameterBinding binding = paramBindings.getBinding( parameter );
binding.setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> setParameter(String name, Object value, TemporalType temporalType) {
locateParameterRegistration( name ).bindValue( value, temporalType );
final QueryParameterBinding binding = paramBindings.getBinding( getParameterMetadata().getQueryParameter( name ) );
binding.setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> setParameter(int position, Object value, TemporalType temporalType) {
locateParameterRegistration( position ).bindValue( value, temporalType );
return this;
}
@Override
public ProcedureCallImplementor<R> setParameterList(QueryParameter parameter, Collection values) {
super.setParameterList( parameter, values );
return this;
}
@Override
public ProcedureCallImplementor<R> setParameterList(String name, Collection values) {
super.setParameterList( name, values );
return this;
}
@Override
public ProcedureCallImplementor<R> setParameterList(String name, Collection values, Type type) {
super.setParameterList( name, values, type );
return this;
}
@Override
public ProcedureCallImplementor<R> setParameterList(String name, Object[] values, Type type) {
super.setParameterList( name, values, type );
return this;
}
@Override
public ProcedureCallImplementor<R> setParameterList(String name, Object[] values) {
super.setParameterList( name, values );
final QueryParameterBinding binding = paramBindings.getBinding( getParameterMetadata().getQueryParameter( position ) );
binding.setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> setParameter(Parameter parameter, Calendar value, TemporalType temporalType) {
locateParameterRegistration( parameter ).bindValue( value, temporalType );
final QueryParameterBinding binding = paramBindings.getBinding( getParameterMetadata().resolve( parameter ) );
binding.setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> setParameter(Parameter parameter, Date value, TemporalType temporalType) {
locateParameterRegistration( parameter ).bindValue( value, temporalType );
final QueryParameterBinding binding = paramBindings.getBinding( getParameterMetadata().resolve( parameter ) );
binding.setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> setParameter(String name, Calendar value, TemporalType temporalType) {
locateParameterRegistration( name ).bindValue( value, temporalType );
final QueryParameterBinding binding = paramBindings.getBinding( name );
binding.setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> setParameter(String name, Date value, TemporalType temporalType) {
locateParameterRegistration( name ).bindValue( value, temporalType );
final QueryParameterBinding binding = paramBindings.getBinding( name );
binding.setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> setParameter(int position, Calendar value, TemporalType temporalType) {
locateParameterRegistration( position ).bindValue( value, temporalType );
final QueryParameterBinding binding = paramBindings.getBinding( position );
binding.setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public ProcedureCallImplementor<R> setParameter(int position, Date value, TemporalType temporalType) {
locateParameterRegistration( position ).bindValue( value, temporalType );
final QueryParameterBinding binding = paramBindings.getBinding( position );
binding.setBindValue( value, temporalType );
return this;
}
}

View File

@ -162,7 +162,7 @@ public class ProcedureCallMementoImpl implements ProcedureCallMemento {
registration.getPosition(),
registration.getName(),
registration.getMode(),
registration.getType(),
registration.getParameterType(),
registration.getHibernateType(),
registration.isPassNullsEnabled()
);

View File

@ -7,24 +7,44 @@
package org.hibernate.procedure.spi;
import java.sql.CallableStatement;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.procedure.internal.ProcedureParamBindings;
import org.hibernate.query.procedure.internal.ProcedureParameterMetadata;
/**
* @author Steve Ebersole
*/
public interface CallableStatementSupport {
String renderCallableStatement(
default String renderCallableStatement(
String name,
ParameterStrategy parameterStrategy,
List<ParameterRegistrationImplementor<?>> parameterRegistrations,
SharedSessionContractImplementor session);
SharedSessionContractImplementor session) {
throw new UnsupportedOperationException(
"Legacy #renderCallableStatement called but implementation does not support that call."
);
}
default String renderCallableStatement(
String procedureName,
ProcedureParameterMetadata parameterMetadata,
ProcedureParamBindings paramBindings,
SharedSessionContractImplementor session) {
return renderCallableStatement(
procedureName,
parameterMetadata.getParameterStrategy(),
new ArrayList( parameterMetadata.collectAllParameters() ),
session
);
}
void registerParameters(
String procedureName,
CallableStatement statement,
ParameterStrategy parameterStrategy,
List<ParameterRegistrationImplementor<?>> parameterRegistrations,
SharedSessionContractImplementor session);
SharedSessionContractImplementor session);;
}

View File

@ -10,6 +10,7 @@ import java.sql.CallableStatement;
import java.sql.SQLException;
import org.hibernate.procedure.ParameterRegistration;
import org.hibernate.query.QueryParameter;
import org.hibernate.type.Type;
/**

View File

@ -8,6 +8,7 @@ package org.hibernate.query;
import java.util.Collection;
import java.util.Set;
import java.util.function.Consumer;
import javax.persistence.Parameter;
/**
@ -62,4 +63,6 @@ public interface ParameterMetadata {
int getParameterCount();
boolean containsReference(QueryParameter parameter);
void visitRegistrations(Consumer<QueryParameter> action);
}

View File

@ -44,7 +44,6 @@ import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.NonUniqueResultException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.QueryException;
import org.hibernate.QueryParameterException;
import org.hibernate.ScrollMode;
import org.hibernate.TypeMismatchException;
@ -75,6 +74,7 @@ import org.hibernate.query.ParameterMetadata;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterListBinding;
import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.transform.ResultTransformer;
@ -110,7 +110,6 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
private final SharedSessionContractImplementor producer;
private final ParameterMetadata parameterMetadata;
private final QueryParameterBindingsImpl queryParameterBindings;
private FlushMode flushMode;
private CacheStoreMode cacheStoreMode;
@ -140,11 +139,6 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
ParameterMetadata parameterMetadata) {
this.producer = producer;
this.parameterMetadata = parameterMetadata;
this.queryParameterBindings = QueryParameterBindingsImpl.from(
parameterMetadata,
producer.getFactory(),
producer.isQueryParametersValidationEnabled()
);
}
@Override
@ -394,28 +388,32 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@Override
public QueryImplementor<R> setParameter(int position, OffsetDateTime value, TemporalType temporalType) {
locateBinding( position ).setBindValue( value, temporalType );
final QueryParameterBinding<Object> binding = getQueryParameterBindings().getBinding(
getParameterMetadata().getQueryParameter( position )
);
binding.setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public <P> QueryImplementor setParameter(QueryParameter<P> parameter, P value) {
queryParameterBindings.getBinding( (QueryParameter) parameter );
locateBinding( parameter ).setBindValue( value );
getQueryParameterBindings().getBinding( (QueryParameter) parameter ).setBindValue( value );
return this;
}
@SuppressWarnings("unchecked")
private <P> QueryParameterBinding<P> locateBinding(Parameter<P> parameter) {
if ( parameter instanceof QueryParameter ) {
return queryParameterBindings.getBinding( (QueryParameter) parameter );
return getQueryParameterBindings().getBinding( (QueryParameter) parameter );
}
else if ( parameter.getName() != null ) {
return queryParameterBindings.getBinding( parameter.getName() );
return getQueryParameterBindings().getBinding( parameter.getName() );
}
else if ( parameter.getPosition() != null ) {
return queryParameterBindings.getBinding( parameter.getPosition() );
return getQueryParameterBindings().getBinding( parameter.getPosition() );
}
throw getExceptionConverter().convert(
@ -424,15 +422,15 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
}
private <P> QueryParameterBinding<P> locateBinding(QueryParameter<P> parameter) {
return queryParameterBindings.getBinding( parameter );
return getQueryParameterBindings().getBinding( parameter );
}
private <P> QueryParameterBinding<P> locateBinding(String name) {
return queryParameterBindings.getBinding( name );
return getQueryParameterBindings().getBinding( name );
}
private <P> QueryParameterBinding<P> locateBinding(int position) {
return queryParameterBindings.getBinding( position );
return getQueryParameterBindings().getBinding( position );
}
@Override
@ -470,15 +468,15 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
private QueryParameterListBinding locateListBinding(Parameter parameter) {
if ( parameter instanceof QueryParameter ) {
return queryParameterBindings.getQueryParameterListBinding( (QueryParameter) parameter );
return getQueryParameterBindings().getQueryParameterListBinding( (QueryParameter) parameter );
}
else {
return queryParameterBindings.getQueryParameterListBinding( parameter.getName() );
return getQueryParameterBindings().getQueryParameterListBinding( parameter.getName() );
}
}
private QueryParameterListBinding locateListBinding(String name) {
return queryParameterBindings.getQueryParameterListBinding( name );
return getQueryParameterBindings().getQueryParameterListBinding( name );
}
@Override
@ -493,7 +491,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
setParameterList( name, (Collection) value );
}
else {
queryParameterBindings.getBinding( name ).setBindValue( value );
getQueryParameterBindings().getBinding( name ).setBindValue( value );
}
return this;
@ -508,10 +506,10 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
setParameter( position, typedParameterValue.getValue(), typedParameterValue.getType() );
}
else if ( value instanceof Collection && !isRegisteredAsBasicType( value.getClass() ) ) {
setParameterList( parameterMetadata.getQueryParameter( position ), (Collection) value );
setParameterList( getParameterMetadata().getQueryParameter( position ), (Collection) value );
}
else {
queryParameterBindings.getBinding( position ).setBindValue( value );
getQueryParameterBindings().getBinding( position ).setBindValue( value );
}
return this;
}
@ -519,105 +517,105 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@Override
@SuppressWarnings("unchecked")
public <P> QueryImplementor setParameter(QueryParameter<P> parameter, P value, Type type) {
queryParameterBindings.getBinding( parameter ).setBindValue( value, type );
getQueryParameterBindings().getBinding( parameter ).setBindValue( value, type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(String name, Object value, Type type) {
queryParameterBindings.getBinding( name ).setBindValue( value, type );
getQueryParameterBindings().getBinding( name ).setBindValue( value, type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(int position, Object value, Type type) {
queryParameterBindings.getBinding( position ).setBindValue( value, type );
getQueryParameterBindings().getBinding( position ).setBindValue( value, type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public <P> QueryImplementor setParameter(QueryParameter<P> parameter, P value, TemporalType temporalType) {
queryParameterBindings.getBinding( parameter ).setBindValue( value, temporalType );
getQueryParameterBindings().getBinding( parameter ).setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(String name, Object value, TemporalType temporalType) {
queryParameterBindings.getBinding( name ).setBindValue( value, temporalType );
getQueryParameterBindings().getBinding( name ).setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(int position, Object value, TemporalType temporalType) {
queryParameterBindings.getBinding( position ).setBindValue( value, temporalType );
getQueryParameterBindings().getBinding( position ).setBindValue( value, temporalType );
return this;
}
@Override
@SuppressWarnings("unchecked")
public <P> QueryImplementor<R> setParameterList(QueryParameter<P> parameter, Collection<P> values) {
queryParameterBindings.getQueryParameterListBinding( parameter ).setBindValues( values );
getQueryParameterBindings().getQueryParameterListBinding( parameter ).setBindValues( values );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(String name, Collection values) {
queryParameterBindings.getQueryParameterListBinding( name ).setBindValues( values );
getQueryParameterBindings().getQueryParameterListBinding( name ).setBindValues( values );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(int position, Collection values) {
queryParameterBindings.getQueryParameterListBinding( position ).setBindValues( values );
getQueryParameterBindings().getQueryParameterListBinding( position ).setBindValues( values );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(String name, Collection values, Type type) {
queryParameterBindings.getQueryParameterListBinding( name ).setBindValues( values, type );
getQueryParameterBindings().getQueryParameterListBinding( name ).setBindValues( values, type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(int position, Collection values, Type type) {
queryParameterBindings.getQueryParameterListBinding( position ).setBindValues( values, type );
getQueryParameterBindings().getQueryParameterListBinding( position ).setBindValues( values, type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(String name, Object[] values, Type type) {
queryParameterBindings.getQueryParameterListBinding( name ).setBindValues( Arrays.asList( values ), type );
getQueryParameterBindings().getQueryParameterListBinding( name ).setBindValues( Arrays.asList( values ), type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(int position, Object[] values, Type type) {
queryParameterBindings.getQueryParameterListBinding( position ).setBindValues( Arrays.asList( values ), type );
getQueryParameterBindings().getQueryParameterListBinding( position ).setBindValues( Arrays.asList( values ), type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(String name, Object[] values) {
queryParameterBindings.getQueryParameterListBinding( name ).setBindValues( Arrays.asList( values ) );
getQueryParameterBindings().getQueryParameterListBinding( name ).setBindValues( Arrays.asList( values ) );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(int position, Object[] values) {
queryParameterBindings.getQueryParameterListBinding( position ).setBindValues( Arrays.asList( values ) );
getQueryParameterBindings().getQueryParameterListBinding( position ).setBindValues( Arrays.asList( values ) );
return this;
}
@ -625,7 +623,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(Parameter<Calendar> param, Calendar value, TemporalType temporalType) {
getProducer().checkOpen();
queryParameterBindings.getBinding( (QueryParameter) param ).setBindValue( value, temporalType );
getQueryParameterBindings().getBinding( (QueryParameter) param ).setBindValue( value, temporalType );
return this;
}
@ -633,7 +631,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(Parameter<Date> param, Date value, TemporalType temporalType) {
getProducer().checkOpen();
queryParameterBindings.getBinding( (QueryParameter) param ).setBindValue( value, temporalType );
getQueryParameterBindings().getBinding( (QueryParameter) param ).setBindValue( value, temporalType );
return this;
}
@ -641,7 +639,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(String name, Calendar value, TemporalType temporalType) {
getProducer().checkOpen();
queryParameterBindings.getBinding( name ).setBindValue( value, temporalType );
getQueryParameterBindings().getBinding( name ).setBindValue( value, temporalType );
return this;
}
@ -649,7 +647,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(String name, Date value, TemporalType temporalType) {
getProducer().checkOpen();
queryParameterBindings.getBinding( name ).setBindValue( value, temporalType );
getQueryParameterBindings().getBinding( name ).setBindValue( value, temporalType );
return this;
}
@ -657,7 +655,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(int position, Calendar value, TemporalType temporalType) {
getProducer().checkOpen();
queryParameterBindings.getBinding( position ).setBindValue( value, temporalType );
getQueryParameterBindings().getBinding( position ).setBindValue( value, temporalType );
return this;
}
@ -665,7 +663,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(int position, Date value, TemporalType temporalType) {
getProducer().checkOpen();
queryParameterBindings.getBinding( position ).setBindValue( value, temporalType );
getQueryParameterBindings().getBinding( position ).setBindValue( value, temporalType );
return this;
}
@ -676,7 +674,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
}
@Override
public Parameter<?> getParameter(String name) {
public QueryParameter<?> getParameter(String name) {
getProducer().checkOpen( false );
try {
return getParameterMetadata().getQueryParameter( name );
@ -688,7 +686,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@Override
@SuppressWarnings("unchecked")
public <T> Parameter<T> getParameter(String name, Class<T> type) {
public <T> QueryParameter<T> getParameter(String name, Class<T> type) {
try {
final QueryParameter parameter = getParameterMetadata().getQueryParameter( name );
if ( !parameter.getParameterType().isAssignableFrom( type ) ) {
@ -706,7 +704,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
}
@Override
public Parameter<?> getParameter(int position) {
public QueryParameter<?> getParameter(int position) {
// It is important to understand that there are 2 completely distinct conceptualization of
// "positional parameters" in play here:
// 1) The legacy Hibernate concept is akin to JDBC PreparedStatement parameters. Very limited and
@ -739,7 +737,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@Override
@SuppressWarnings("unchecked")
public <T> Parameter<T> getParameter(int position, Class<T> type) {
public <T> QueryParameter<T> getParameter(int position, Class<T> type) {
try {
final QueryParameter parameter = getParameterMetadata().getQueryParameter( position );
if ( !parameter.getParameterType().isAssignableFrom( type ) ) {
@ -759,18 +757,20 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@Override
public boolean isBound(Parameter<?> parameter) {
getProducer().checkOpen();
return queryParameterBindings.isBound( (QueryParameter) parameter );
return getQueryParameterBindings().isBound( (QueryParameter) parameter );
}
@Override
public <T> T getParameterValue(Parameter<T> parameter) {
LOGGER.tracef( "#getParameterValue(%s)", parameter );
getProducer().checkOpen( false );
if ( !parameterMetadata.containsReference( (QueryParameter) parameter ) ) {
if ( !getParameterMetadata().containsReference( (QueryParameter) parameter ) ) {
throw new IllegalArgumentException( "Parameter reference [" + parameter + "] did not come from this query" );
}
final QueryParameterBinding<T> binding = queryParameterBindings.getBinding( (QueryParameter<T>) parameter );
final QueryParameterBinding<T> binding = getQueryParameterBindings().getBinding( (QueryParameter<T>) parameter );
LOGGER.debugf( "Checking whether parameter reference [%s] is bound : %s", parameter, binding.isBound() );
if ( !binding.isBound() ) {
throw new IllegalStateException( "Parameter value not yet bound : " + parameter.toString() );
@ -784,7 +784,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
final QueryParameterBinding binding;
try {
binding = queryParameterBindings.getBinding( name );
binding = getQueryParameterBindings().getBinding( name );
}
catch (QueryParameterException e) {
throw new IllegalArgumentException( "Could not resolve parameter by name - " + name, e );
@ -803,7 +803,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
final QueryParameterBinding binding;
try {
binding = queryParameterBindings.getBinding( position );
binding = getQueryParameterBindings().getBinding( position );
}
catch (QueryParameterException e) {
throw new IllegalArgumentException( "Could not resolve parameter by position - " + position, e );
@ -849,7 +849,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
}
protected Type determineType(String namedParam, Class retType) {
Type type = queryParameterBindings.getBinding( namedParam ).getBindType();
Type type = getQueryParameterBindings().getBinding( namedParam ).getBindType();
if ( type == null ) {
type = getParameterMetadata().getQueryParameter( namedParam ).getType();
}
@ -1331,8 +1331,8 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
if ( cls.isInstance( getParameterMetadata() ) ) {
return (T) getParameterMetadata();
}
if ( cls.isInstance( queryParameterBindings ) ) {
return (T) queryParameterBindings;
if ( cls.isInstance( getQueryParameterBindings() ) ) {
return (T) getQueryParameterBindings();
}
if ( cls.isInstance( this ) ) {
return (T) this;
@ -1358,7 +1358,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
}
QueryParameters queryParameters = new QueryParameters(
queryParameterBindings,
getQueryParameterBindings(),
getLockOptions(),
queryOptions,
true,
@ -1381,23 +1381,23 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
}
public QueryParameters getQueryParameters() {
final String expandedQuery = queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() );
final String expandedQuery = getQueryParameterBindings().expandListValuedParameters( getQueryString(), getProducer() );
return makeQueryParametersForExecution( expandedQuery );
}
@SuppressWarnings("deprecation")
protected Type[] getPositionalParameterTypes() {
return queryParameterBindings.collectPositionalBindTypes();
return getQueryParameterBindings().collectPositionalBindTypes();
}
@SuppressWarnings("deprecation")
protected Object[] getPositionalParameterValues() {
return queryParameterBindings.collectPositionalBindValues();
return getQueryParameterBindings().collectPositionalBindValues();
}
@SuppressWarnings("deprecation")
protected Map<String, TypedValue> getNamedParameterMap() {
return queryParameterBindings.collectNamedParameterBindings();
return getQueryParameterBindings().collectNamedParameterBindings();
}
private FlushMode sessionFlushMode;
@ -1405,7 +1405,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
protected void beforeQuery() {
if ( optionalId == null ) {
queryParameterBindings.verifyParametersBound( isCallable() );
getQueryParameterBindings().verifyParametersBound( isCallable() );
}
assert sessionFlushMode == null;
@ -1450,7 +1450,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
return EmptyIterator.INSTANCE;
}
return getProducer().iterate(
queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() ),
getQueryParameterBindings().expandListValuedParameters( getQueryString(), getProducer() ),
getQueryParameters()
);
}
@ -1475,7 +1475,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
if (getMaxResults() == 0){
return EmptyScrollableResults.INSTANCE;
}
final String query = queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() );
final String query = getQueryParameterBindings().expandListValuedParameters( getQueryString(), getProducer() );
QueryParameters queryParameters = makeQueryParametersForExecution( query );
queryParameters.setScrollMode( scrollMode );
return getProducer().scroll( query, queryParameters );
@ -1538,16 +1538,14 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
}
}
final String expandedQuery = queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() );
final String expandedQuery = getQueryParameterBindings().expandListValuedParameters( getQueryString(), getProducer() );
return getProducer().list(
expandedQuery,
makeQueryParametersForExecution( expandedQuery )
);
}
public QueryParameterBindingsImpl getQueryParameterBindings() {
return queryParameterBindings;
}
protected abstract QueryParameterBindings getQueryParameterBindings();
@Override
public R uniqueResult() {
@ -1620,7 +1618,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
}
protected int doExecuteUpdate() {
final String expandedQuery = queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() );
final String expandedQuery = getQueryParameterBindings().expandListValuedParameters( getQueryString(), getProducer() );
return getProducer().executeUpdate(
expandedQuery,
makeQueryParametersForExecution( expandedQuery )

View File

@ -14,6 +14,7 @@ import org.hibernate.ScrollMode;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.Query;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.type.Type;
@ -25,6 +26,7 @@ import org.hibernate.type.Type;
public class CollectionFilterImpl extends org.hibernate.query.internal.AbstractProducedQuery {
private final String queryString;
private Object collection;
private final QueryParameterBindingsImpl queryParameterBindings;
public CollectionFilterImpl(
String queryString,
@ -34,6 +36,16 @@ public class CollectionFilterImpl extends org.hibernate.query.internal.AbstractP
super( session, parameterMetadata );
this.queryString = queryString;
this.collection = collection;
this.queryParameterBindings = QueryParameterBindingsImpl.from(
parameterMetadata,
session.getFactory(),
session.isQueryParametersValidationEnabled()
);
}
@Override
protected QueryParameterBindings getQueryParameterBindings() {
return queryParameterBindings;
}
@Override

View File

@ -44,6 +44,7 @@ import org.hibernate.query.NativeQuery;
import org.hibernate.query.ParameterMetadata;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.spi.NativeQueryImplementor;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.Type;
@ -55,6 +56,7 @@ import static org.hibernate.jpa.QueryHints.HINT_NATIVE_LOCKMODE;
*/
public class NativeQueryImpl<T> extends AbstractProducedQuery<T> implements NativeQueryImplementor<T> {
private final String sqlString;
private final QueryParameterBindingsImpl queryParameterBindings;
private List<NativeSQLQueryReturn> queryReturns;
private List<NativeQueryReturnBuilder> queryReturnBuilders;
private boolean autoDiscoverTypes;
@ -100,6 +102,13 @@ public class NativeQueryImpl<T> extends AbstractProducedQuery<T> implements Nati
else {
this.queryReturns = new ArrayList<>();
}
this.queryParameterBindings = QueryParameterBindingsImpl.from(
parameterMetadata,
session.getFactory(),
session.isQueryParametersValidationEnabled()
);
}
public NativeQueryImpl(
@ -113,6 +122,17 @@ public class NativeQueryImpl<T> extends AbstractProducedQuery<T> implements Nati
this.sqlString = sqlString;
this.callable = callable;
this.querySpaces = new ArrayList<>();
this.queryParameterBindings = QueryParameterBindingsImpl.from(
sqlParameterMetadata,
session.getFactory(),
session.isQueryParametersValidationEnabled()
);
}
@Override
protected QueryParameterBindings getQueryParameterBindings() {
return queryParameterBindings;
}
@Override

View File

@ -14,6 +14,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import javax.persistence.Parameter;
import org.hibernate.QueryException;
@ -197,14 +198,6 @@ public class ParameterMetadataImpl implements ParameterMetadata {
return (QueryParameter<T>) param;
}
if ( param.getName() != null ) {
return getQueryParameter( param.getName() );
}
if ( param.getPosition() != null ) {
return getQueryParameter( param.getPosition() );
}
throw new IllegalArgumentException( "Could not resolve javax.persistence.Parameter to org.hibernate.query.QueryParameter" );
}
@ -232,6 +225,20 @@ public class ParameterMetadataImpl implements ParameterMetadata {
return descriptor;
}
@Override
public void visitRegistrations(Consumer<QueryParameter> action) {
if ( hasPositionalParameters() ) {
for ( OrdinalParameterDescriptor descriptor : ordinalDescriptorMap.values() ) {
action.accept( descriptor );
}
}
else if ( hasNamedParameters() ) {
for ( NamedParameterDescriptor descriptor : namedDescriptorMap.values() ) {
action.accept( descriptor );
}
}
}
/**
* Deprecated.
*

View File

@ -9,6 +9,7 @@ package org.hibernate.query.internal;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.ParameterMetadata;
import org.hibernate.query.Query;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.type.Type;
/**
@ -17,12 +18,24 @@ import org.hibernate.type.Type;
public class QueryImpl<R> extends AbstractProducedQuery<R> implements Query<R> {
private final String queryString;
private final QueryParameterBindingsImpl queryParameterBindings;
public QueryImpl(
SharedSessionContractImplementor producer,
ParameterMetadata parameterMetadata,
String queryString) {
super( producer, parameterMetadata );
this.queryString = queryString;
this.queryParameterBindings = QueryParameterBindingsImpl.from(
parameterMetadata,
producer.getFactory(),
producer.isQueryParametersValidationEnabled()
);
}
@Override
protected QueryParameterBindings getQueryParameterBindings() {
return queryParameterBindings;
}
@Override

View File

@ -18,7 +18,7 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
public abstract class QueryParameterImpl<T> implements QueryParameter<T> {
private final Type expectedType;
private Type expectedType;
public QueryParameterImpl(Type expectedType) {
this.expectedType = expectedType;
@ -29,6 +29,10 @@ public abstract class QueryParameterImpl<T> implements QueryParameter<T> {
return expectedType;
}
public void setHibernateType(Type expectedType) {
this.expectedType = expectedType;
}
@Override
public Class<T> getParameterType() {
return expectedType == null ? null : expectedType.getReturnedClass();

View File

@ -0,0 +1,135 @@
/*
* 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.query.procedure.internal;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.ParameterMode;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.procedure.ParameterBind;
import org.hibernate.procedure.internal.ParameterBindImpl;
import org.hibernate.procedure.internal.ProcedureCallImpl;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.procedure.spi.ProcedureParameterImplementor;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterListBinding;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public class ProcedureParamBindings implements QueryParameterBindings {
private final ProcedureParameterMetadata parameterMetadata;
private final ProcedureCallImpl procedureCall;
private final Map<ProcedureParameterImplementor, ParameterBind> bindingMap = new HashMap<>();
public ProcedureParamBindings(
ProcedureParameterMetadata parameterMetadata,
ProcedureCallImpl procedureCall) {
this.parameterMetadata = parameterMetadata;
this.procedureCall = procedureCall;
}
public ProcedureParameterMetadata getParameterMetadata() {
return parameterMetadata;
}
public ProcedureCallImpl getProcedureCall() {
return procedureCall;
}
@Override
public boolean isBound(QueryParameter parameter) {
return getBinding( parameter ).isBound();
}
@Override
public <T> QueryParameterBinding<T> getBinding(QueryParameter<T> parameter) {
final ProcedureParameterImplementor<T> procParam = parameterMetadata.resolve( parameter );
ParameterBind binding = bindingMap.get( procParam );
if ( binding == null ) {
if ( ! parameterMetadata.containsReference( parameter ) ) {
throw new IllegalArgumentException( "Passed parameter is not registered with this query" );
}
binding = new ParameterBindImpl( procParam, this );
bindingMap.put( procParam, binding );
}
return binding;
}
@Override
public <T> QueryParameterBinding<T> getBinding(String name) {
return getBinding( parameterMetadata.getQueryParameter( name ) );
}
@Override
public <T> QueryParameterBinding<T> getBinding(int position) {
return getBinding( parameterMetadata.getQueryParameter( position ) );
}
@Override
public void verifyParametersBound(boolean callable) {
parameterMetadata.visitRegistrations(
queryParameter -> {
final ProcedureParameterImplementor procParam = (ProcedureParameterImplementor) queryParameter;
if ( procParam.getMode() == ParameterMode.IN
|| procParam.getMode() == ParameterMode.INOUT ) {
if ( !getBinding( procParam ).isBound() ) {
// depending on "pass nulls" this might be ok...
// for now, just log a warning
}
}
}
);
}
@Override
public String expandListValuedParameters(String queryString, SharedSessionContractImplementor producer) {
return queryString;
}
@Override
public <T> QueryParameterListBinding<T> getQueryParameterListBinding(QueryParameter<T> parameter) {
return null;
}
@Override
public <T> QueryParameterListBinding<T> getQueryParameterListBinding(String name) {
return null;
}
@Override
public <T> QueryParameterListBinding<T> getQueryParameterListBinding(int position) {
return null;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// I think these are not needed for proc call execution
@Override
public Type[] collectPositionalBindTypes() {
return new Type[0];
}
@Override
public Object[] collectPositionalBindValues() {
return new Object[0];
}
@Override
public Map<String, TypedValue> collectNamedParameterBindings() {
return null;
}
}

View File

@ -6,36 +6,101 @@
*/
package org.hibernate.query.procedure.internal;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Calendar;
import javax.persistence.ParameterMode;
import javax.persistence.TemporalType;
import org.hibernate.procedure.spi.ParameterRegistrationImplementor;
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.procedure.ParameterBind;
import org.hibernate.procedure.ParameterMisuseException;
import org.hibernate.procedure.ParameterRegistration;
import org.hibernate.procedure.internal.ProcedureCallImpl;
import org.hibernate.procedure.spi.ParameterStrategy;
import org.hibernate.query.internal.QueryParameterImpl;
import org.hibernate.query.procedure.spi.ProcedureParameterImplementor;
import org.hibernate.type.CalendarDateType;
import org.hibernate.type.CalendarTimeType;
import org.hibernate.type.CalendarType;
import org.hibernate.type.ProcedureParameterExtractionAware;
import org.hibernate.type.ProcedureParameterNamedBinder;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class ProcedureParameterImpl<T> extends QueryParameterImpl<T> implements ProcedureParameterImplementor<T> {
private ParameterRegistrationImplementor<T> nativeParamRegistration;
public class ProcedureParameterImpl<T>
extends QueryParameterImpl<T>
implements ProcedureParameterImplementor<T>, ParameterRegistration<T> {
private static final Logger log = Logger.getLogger( ProcedureParameterImpl.class );
public ProcedureParameterImpl(ParameterRegistrationImplementor<T> nativeParamRegistration) {
super( nativeParamRegistration.getHibernateType() );
this.nativeParamRegistration = nativeParamRegistration;
private final ProcedureCallImpl procedureCall;
private final String name;
private final Integer position;
private final ParameterMode mode;
private final Class<T> javaType;
private int[] sqlTypes;
private boolean passNullsEnabled;
// in-flight state needed between prepare and extract
private int startIndex;
public ProcedureParameterImpl(
ProcedureCallImpl procedureCall,
String name,
ParameterMode mode,
Class<T> javaType,
Type hibernateType,
boolean initialPassNullsSetting) {
super( hibernateType );
this.procedureCall = procedureCall;
this.name = name;
this.position = null;
this.mode = mode;
this.javaType = javaType;
this.passNullsEnabled = initialPassNullsSetting;
setHibernateType( hibernateType );
}
public ProcedureParameterImpl(
ProcedureCallImpl procedureCall,
Integer position,
ParameterMode mode,
Class<T> javaType,
Type hibernateType,
boolean initialPassNullsSetting) {
super( hibernateType );
this.procedureCall = procedureCall;
this.name = null;
this.position = position;
this.mode = mode;
this.javaType = javaType;
this.passNullsEnabled = initialPassNullsSetting;
setHibernateType( hibernateType );
}
@Override
public ParameterMode getMode() {
return nativeParamRegistration.getMode();
return mode;
}
@Override
public boolean isPassNullsEnabled() {
return nativeParamRegistration.isPassNullsEnabled();
return passNullsEnabled;
}
@Override
public void enablePassingNulls(boolean enabled) {
nativeParamRegistration.enablePassingNulls( enabled );
this.passNullsEnabled = enabled;
}
@Override
@ -45,16 +110,287 @@ public class ProcedureParameterImpl<T> extends QueryParameterImpl<T> implements
@Override
public String getName() {
return nativeParamRegistration.getName();
return name;
}
@Override
public Integer getPosition() {
return nativeParamRegistration.getPosition();
return position;
}
@Override
public ParameterRegistrationImplementor<T> getNativeParameterRegistration() {
return nativeParamRegistration;
public Type getHibernateType() {
return getType();
}
@Override
public void setHibernateType(Type expectedType) {
super.setHibernateType( expectedType );
if ( mode == ParameterMode.REF_CURSOR ) {
sqlTypes = new int[] { Types.REF_CURSOR };
}
else {
if ( expectedType == null ) {
throw new IllegalArgumentException( "Type cannot be null" );
}
else {
sqlTypes = expectedType.sqlTypes( procedureCall.getSession().getFactory() );
}
}
}
@Override
public Class<T> getParameterType() {
return javaType;
}
@Override
public ParameterBind<T> getBind() {
return (ParameterBind<T>) procedureCall.getQueryParameterBindings().getBinding( this );
}
@Override
@SuppressWarnings("unchecked")
public void bindValue(Object value) {
getBind().setBindValue( (T) value );
}
@Override
@SuppressWarnings("unchecked")
public void bindValue(Object value, TemporalType explicitTemporalType) {
getBind().setBindValue( (T) value, explicitTemporalType );
}
@Override
public void prepare(CallableStatement statement, int startIndex) throws SQLException {
// initially set up the Type we will use for binding as the explicit type.
Type typeToUse = getHibernateType();
int[] sqlTypesToUse = sqlTypes;
final ParameterBind bind = getBind();
// however, for Calendar binding with an explicit TemporalType we may need to adjust this...
if ( bind != null && bind.getExplicitTemporalType() != null ) {
if ( Calendar.class.isInstance( bind.getValue() ) ) {
switch ( bind.getExplicitTemporalType() ) {
case TIMESTAMP: {
typeToUse = CalendarType.INSTANCE;
sqlTypesToUse = typeToUse.sqlTypes( procedureCall.getSession().getFactory() );
break;
}
case DATE: {
typeToUse = CalendarDateType.INSTANCE;
sqlTypesToUse = typeToUse.sqlTypes( procedureCall.getSession().getFactory() );
break;
}
case TIME: {
typeToUse = CalendarTimeType.INSTANCE;
sqlTypesToUse = typeToUse.sqlTypes( procedureCall.getSession().getFactory() );
break;
}
}
}
}
this.startIndex = startIndex;
if ( mode == ParameterMode.IN || mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) {
if ( mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) {
if ( sqlTypesToUse.length > 1 ) {
// there is more than one column involved; see if the Hibernate Type can handle
// multi-param extraction...
final boolean canHandleMultiParamExtraction =
ProcedureParameterExtractionAware.class.isInstance( typeToUse )
&& ( (ProcedureParameterExtractionAware) typeToUse ).canDoExtraction();
if ( ! canHandleMultiParamExtraction ) {
// it cannot...
throw new UnsupportedOperationException(
"Type [" + typeToUse + "] does support multi-parameter value extraction"
);
}
}
// TODO: sqlTypesToUse.length > 1 does not seem to have a working use case (HHH-10769).
// The idea is that an embeddable/custom type can have more than one column values
// that correspond with embeddable/custom attribute value. This does not seem to
// be working yet. For now, if sqlTypesToUse.length > 1, then register
// the out parameters by position (since we only have one name).
// This will cause a failure if there are other parameters bound by
// name and the dialect does not support "mixed" named/positional parameters;
// e.g., Oracle.
if ( sqlTypesToUse.length == 1 &&
procedureCall.getParameterStrategy() == ParameterStrategy.NAMED &&
canDoNameParameterBinding( typeToUse ) ) {
statement.registerOutParameter( getName(), sqlTypesToUse[0] );
}
else {
for ( int i = 0; i < sqlTypesToUse.length; i++ ) {
statement.registerOutParameter( startIndex + i, sqlTypesToUse[i] );
}
}
}
if ( mode == ParameterMode.INOUT || mode == ParameterMode.IN ) {
if ( bind == null || bind.getValue() == null ) {
// the user did not bind a value to the parameter being processed. This is the condition
// defined by `passNulls` and that value controls what happens here. If `passNulls` is
// {@code true} we will bind the NULL value into the statement; if `passNulls` is
// {@code false} we will not.
//
// Unfortunately there is not a way to reliably know through JDBC metadata whether a procedure
// parameter defines a default value. Deferring to that information would be the best option
if ( isPassNullsEnabled() ) {
log.debugf(
"Stored procedure [%s] IN/INOUT parameter [%s] not bound and `passNulls` was set to true; binding NULL",
procedureCall.getProcedureName(),
this
);
if ( this.procedureCall.getParameterStrategy() == ParameterStrategy.NAMED && canDoNameParameterBinding( typeToUse ) ) {
((ProcedureParameterNamedBinder) typeToUse).nullSafeSet(
statement,
null,
this.getName(),
procedureCall.getSession()
);
}
else {
typeToUse.nullSafeSet( statement, null, startIndex, procedureCall.getSession() );
}
}
else {
log.debugf(
"Stored procedure [%s] IN/INOUT parameter [%s] not bound and `passNulls` was set to false; assuming procedure defines default value",
procedureCall.getProcedureName(),
this
);
}
}
else {
if ( this.procedureCall.getParameterStrategy() == ParameterStrategy.NAMED && canDoNameParameterBinding( typeToUse ) ) {
((ProcedureParameterNamedBinder) typeToUse).nullSafeSet(
statement,
bind.getValue(),
this.getName(),
procedureCall.getSession()
);
}
else {
typeToUse.nullSafeSet( statement, bind.getValue(), startIndex, procedureCall.getSession() );
}
}
}
}
else {
// we have a REF_CURSOR type param
if ( procedureCall.getParameterStrategy() == ParameterStrategy.NAMED ) {
procedureCall.getSession().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class )
.registerRefCursorParameter( statement, getName() );
}
else {
procedureCall.getSession().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class )
.registerRefCursorParameter( statement, startIndex );
}
}
}
private boolean canDoNameParameterBinding(Type hibernateType) {
final ExtractedDatabaseMetaData databaseMetaData = procedureCall.getSession()
.getJdbcCoordinator()
.getJdbcSessionOwner()
.getJdbcSessionContext()
.getServiceRegistry().getService( JdbcEnvironment.class )
.getExtractedDatabaseMetaData();
return
databaseMetaData.supportsNamedParameters()
&& ProcedureParameterNamedBinder.class.isInstance( hibernateType )
&& ((ProcedureParameterNamedBinder) hibernateType).canDoSetting();
}
public int[] getSqlTypes() {
if ( mode == ParameterMode.REF_CURSOR ) {
// we could use the Types#REF_CURSOR added in Java 8, but that would require requiring Java 8...
throw new IllegalStateException( "REF_CURSOR parameters do not have a SQL/JDBC type" );
}
return determineHibernateType().sqlTypes( procedureCall.getSession().getFactory() );
}
private Type determineHibernateType() {
final ParameterBind<T> bind = getBind();
// if the bind defines a type, that should be the most specific...
final Type bindType = bind.getBindType();
if ( bindType != null ) {
return bindType;
}
// Next, see if the parameter itself has an expected type, and if so use that...
final Type paramType = getHibernateType();
if ( paramType != null ) {
return paramType;
}
// here we just have guessing games
if ( bind.getValue() != null ) {
return procedureCall.getSession()
.getFactory()
.getTypeResolver()
.heuristicType( bind.getValue().getClass().getName() );
}
throw new IllegalStateException( "Unable to determine SQL type(s) - Hibernate Type not known" );
}
@Override
@SuppressWarnings("unchecked")
public T extract(CallableStatement statement) {
if ( mode == ParameterMode.IN ) {
throw new ParameterMisuseException( "IN parameter not valid for output extraction" );
}
final Type hibernateType = determineHibernateType();
final int[] sqlTypes = hibernateType.sqlTypes( procedureCall.getSession().getFactory() );
// TODO: sqlTypesToUse.length > 1 does not seem to have a working use case (HHH-10769).
// For now, if sqlTypes.length > 1 with a named parameter, then extract
// parameter values by position (since we only have one name).
final boolean useNamed = sqlTypes.length == 1 &&
procedureCall.getParameterStrategy() == ParameterStrategy.NAMED &&
canDoNameParameterBinding( hibernateType );
try {
if ( ProcedureParameterExtractionAware.class.isInstance( hibernateType ) ) {
if ( useNamed ) {
return (T) ( (ProcedureParameterExtractionAware) hibernateType ).extract(
statement,
new String[] { getName() },
procedureCall.getSession()
);
}
else {
return (T) ( (ProcedureParameterExtractionAware) hibernateType ).extract(
statement,
startIndex,
procedureCall.getSession()
);
}
}
else {
if ( useNamed ) {
return (T) statement.getObject( name );
}
else {
return (T) statement.getObject( startIndex );
}
}
}
catch (SQLException e) {
throw procedureCall.getSession().getFactory().getSQLExceptionHelper().convert(
e,
"Unable to extract OUT/INOUT parameter value"
);
}
}
}

View File

@ -12,10 +12,13 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.persistence.Parameter;
import org.hibernate.QueryParameterException;
import org.hibernate.procedure.internal.ProcedureCallImpl;
import org.hibernate.procedure.spi.ParameterRegistrationImplementor;
import org.hibernate.procedure.spi.ParameterStrategy;
import org.hibernate.query.ParameterMetadata;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.procedure.ProcedureParameter;
@ -25,35 +28,46 @@ import org.hibernate.query.procedure.spi.ProcedureParameterImplementor;
* @author Steve Ebersole
*/
public class ProcedureParameterMetadata implements ParameterMetadata {
private List<ProcedureParameterImplementor> parameters;
private final ProcedureCallImpl procedureCall;
private ParameterStrategy parameterStrategy = ParameterStrategy.UNKNOWN;
private List<ProcedureParameterImplementor> parameters = new ArrayList<>();
private boolean hasNamed;
private int ordinalParamCount;
public ProcedureParameterMetadata() {
parameters = new ArrayList<>( );
public ProcedureParameterMetadata(ProcedureCallImpl procedureCall) {
this.procedureCall = procedureCall;
}
public void registerParameter(ProcedureParameterImplementor parameter) {
if ( parameter.getName() != null ) {
if ( parameterStrategy == ParameterStrategy.POSITIONAL ) {
throw new IllegalArgumentException( "Cannot mix named parameter with positional parameter registrations" );
}
parameterStrategy = ParameterStrategy.NAMED;
}
else if ( parameter.getPosition() != null ) {
if ( parameterStrategy == ParameterStrategy.NAMED ) {
throw new IllegalArgumentException( "Cannot mix positional parameter with named parameter registrations" );
}
this.parameterStrategy = ParameterStrategy.POSITIONAL;
}
else {
throw new IllegalArgumentException( "Unrecognized parameter type : " + parameter );
}
if ( parameters == null ) {
parameters = new ArrayList<>();
}
parameters.add( parameter );
this.hasNamed = hasNamed || parameter.getName() != null;
if ( parameter.getPosition() != null ) {
ordinalParamCount++;
}
}
@Override
public boolean hasNamedParameters() {
return hasNamed;
return parameterStrategy == ParameterStrategy.NAMED;
}
@Override
public boolean hasPositionalParameters() {
return ordinalParamCount > 0;
return parameterStrategy == ParameterStrategy.POSITIONAL;
}
@Override
@ -76,7 +90,7 @@ public class ProcedureParameterMetadata implements ParameterMetadata {
@Override
public Set<String> getNamedParameterNames() {
if ( !hasNamed ) {
if ( !hasNamedParameters() ) {
return Collections.emptySet();
}
@ -91,69 +105,53 @@ public class ProcedureParameterMetadata implements ParameterMetadata {
@Override
public int getPositionalParameterCount() {
return ordinalParamCount;
return hasPositionalParameters() ? parameters.size() : 0;
}
@Override
@SuppressWarnings("unchecked")
public <T> QueryParameter<T> getQueryParameter(String name) {
public <T> ParameterRegistrationImplementor<T> getQueryParameter(String name) {
assert name != null;
QueryParameter<T> result = null;
if ( hasNamed ) {
for ( ProcedureParameter parameter : parameters ) {
if ( hasNamedParameters() ) {
for ( ParameterRegistrationImplementor parameter : parameters ) {
if ( name.equals( parameter.getName() ) ) {
result = parameter;
break;
return parameter;
}
}
}
if ( result != null ) {
return result;
}
throw new QueryParameterException( "could not locate named parameter [" + name + "]" );
throw new IllegalArgumentException( "Named parameter [" + name + "] is not registered with this procedure call" );
}
@Override
@SuppressWarnings("unchecked")
public <T> QueryParameter<T> getQueryParameter(Integer position) {
public <T> ParameterRegistrationImplementor<T> getQueryParameter(Integer position) {
assert position != null;
if ( ordinalParamCount > 0 ) {
for ( ProcedureParameter parameter : parameters ) {
if ( hasPositionalParameters() ) {
for ( ParameterRegistrationImplementor parameter : parameters ) {
if ( parameter.getPosition() != null && position.intValue() == parameter.getPosition() ) {
return parameter;
}
}
}
throw new QueryParameterException( "could not locate parameter at position [" + position + "]" );
throw new IllegalArgumentException( "Positional parameter [" + position + "] is not registered with this procedure call" );
}
@Override
@SuppressWarnings("unchecked")
public <T> QueryParameter<T> resolve(Parameter<T> param) {
// first see if that instance exists here...
for ( ProcedureParameter parameter : parameters ) {
public <T> ProcedureParameterImplementor<T> resolve(Parameter<T> param) {
if ( ProcedureParameterImplementor.class.isInstance( param ) ) {
for ( ProcedureParameterImplementor parameter : parameters ) {
if ( parameter == param ) {
return parameter;
}
}
// otherwise, try name/position from the incoming param
if ( param.getPosition() != null || param.getName() != null ) {
for ( ProcedureParameter parameter : parameters ) {
// name
if ( param.getName() != null && param.getName().equals( parameter.getName() ) ) {
return parameter;
}
// position
if ( param.getPosition() != null && param.getPosition().equals( parameter.getPosition() ) ) {
return parameter;
}
}
}
return null;
throw new IllegalArgumentException( "Could not resolve javax.persistence.Parameter to org.hibernate.query.QueryParameter" );
}
@Override
@ -176,4 +174,16 @@ public class ProcedureParameterMetadata implements ParameterMetadata {
public boolean containsReference(QueryParameter parameter) {
return parameters.contains( parameter );
}
public ParameterStrategy getParameterStrategy() {
return parameterStrategy;
}
@Override
public void visitRegistrations(Consumer<QueryParameter> action) {
for ( ProcedureParameterImplementor parameter : parameters ) {
action.accept( parameter );
}
}
}

View File

@ -12,6 +12,5 @@ import org.hibernate.query.procedure.ProcedureParameter;
/**
* @author Steve Ebersole
*/
public interface ProcedureParameterImplementor<T> extends ProcedureParameter<T> {
ParameterRegistrationImplementor<T> getNativeParameterRegistration();
public interface ProcedureParameterImplementor<T> extends ProcedureParameter<T>, ParameterRegistrationImplementor<T> {
}

View File

@ -6,8 +6,13 @@
*/
package org.hibernate.query.spi;
import java.util.Map;
import org.hibernate.Incubating;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.query.QueryParameter;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
@ -19,4 +24,15 @@ public interface QueryParameterBindings {
<T> QueryParameterBinding<T> getBinding(QueryParameter<T> parameter);
<T> QueryParameterBinding<T> getBinding(String name);
<T> QueryParameterBinding<T> getBinding(int position);
void verifyParametersBound(boolean callable);
String expandListValuedParameters(String queryString, SharedSessionContractImplementor producer);
<T> QueryParameterListBinding<T> getQueryParameterListBinding(QueryParameter<T> parameter);
<T> QueryParameterListBinding<T> getQueryParameterListBinding(String name);
<T> QueryParameterListBinding<T> getQueryParameterListBinding(int position);
Type[] collectPositionalBindTypes();
Object[] collectPositionalBindValues();
Map<String,TypedValue> collectNamedParameterBindings();
}

View File

@ -0,0 +1,140 @@
/*
* 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.test.jpa.compliance.tck2_2;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Parameter;
import javax.persistence.ParameterMode;
import javax.persistence.Table;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.query.QueryParameter;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.test.sql.storedproc.StoredProcedureResultSetMappingTest;
import org.junit.Test;
import static org.junit.Assert.fail;
/**
* @author Steve Ebersole
*/
@RequiresDialect( H2Dialect.class )
public class StoredProcedureApiTests extends BaseNonConfigCoreFunctionalTestCase {
@Test
public void parameterValueAccess() {
inTransaction(
session -> {
final ProcedureCall call = session.createStoredProcedureCall( "test" );
call.registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN);
call.registerStoredProcedureParameter( 2, String.class, ParameterMode.OUT);
call.setParameter( 1, 1 );
call.getParameterValue( 1 );
}
);
}
@Test
public void testInvalidParameterReference() {
inTransaction(
session -> {
final ProcedureCall call1 = session.createStoredProcedureCall( "test" );
call1.registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN);
final Parameter<Integer> p1_1 = (Parameter<Integer>) call1.getParameter( 1 );
call1.setParameter( 1, 1 );
final ProcedureCall call2 = session.createStoredProcedureCall( "test" );
call2.registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN);
call2.setParameter( 1, 1 );
try {
call2.getParameterValue( p1_1 );
fail( "Expecting failure" );
}
catch (IllegalArgumentException expected) {
}
}
);
}
@Test
public void testParameterBindTypeMismatch() {
inTransaction(
session -> {
try {
final ProcedureCall call1 = session.createStoredProcedureCall( "test" );
call1.registerStoredProcedureParameter( 1, Integer.class, ParameterMode.IN );
call1.setParameter( 1, new Date() );
fail( "expecting failure" );
}
catch (IllegalArgumentException expected) {
}
}
);
}
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( Person.class );
}
@Override
protected void afterMetadataBuilt(Metadata metadata) {
super.afterMetadataBuilt( metadata );
// metadata.getDatabase().addAuxiliaryDatabaseObject(
// new StoredProcedureResultSetMappingTest.ProcedureDefinition() {
// @Override
// public boolean appliesToDialect(Dialect dialect) {
// return H2Dialect.class.isInstance( dialect );
// }
//
// @Override
// public boolean beforeTablesOnCreation() {
// return false;
// }
//
// @Override
// public String getExportIdentifier() {
// return "StoredProcedure#test"
// }
//
// @Override
// public String[] sqlCreateStrings(Dialect dialect) {
// return super.sqlCreateStrings( dialect );
// }
//
// @Override
// public String[] sqlDropStrings(Dialect dialect) {
// return super.sqlDropStrings( dialect );
// }
// }
// );
}
@Entity( name = "Person" )
@Table( name = "person" )
public static class Person {
@Id
public Integer id;
public String name;
}
}

View File

@ -284,8 +284,10 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
session.beginTransaction();
ProcedureCall query = session.createStoredProcedureCall( "findUserRange" );
query.registerParameter( 1, Integer.class, ParameterMode.IN ).bindValue( 1 );
query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 );
query.registerParameter( 1, Integer.class, ParameterMode.IN )
.bindValue( 1 );
query.registerParameter( 2, Integer.class, ParameterMode.IN )
.bindValue( 2 );
ProcedureOutputs procedureResult = query.getOutputs();
Output currentOutput = procedureResult.getCurrent();
assertNotNull( currentOutput );