HHH-12136 - Various improvements for ProcedureCall/StoredProcedureQuery
This commit is contained in:
parent
15bf44e4e9
commit
6ba328e7a0
|
@ -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 );
|
||||
}
|
||||
;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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,41 +372,48 @@ 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()
|
||||
.getStatementPreparer()
|
||||
.prepareStatement( call, true );
|
||||
LOG.debugf( "Preparing procedure call : %s", call );
|
||||
final CallableStatement statement = (CallableStatement) getSession()
|
||||
.getJdbcCoordinator()
|
||||
.getStatementPreparer()
|
||||
.prepareStatement( call, true );
|
||||
|
||||
|
||||
// prepare parameters
|
||||
int i = 1;
|
||||
// prepare parameters
|
||||
|
||||
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
|
||||
parameter.prepare( statement, i );
|
||||
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
|
||||
i++;
|
||||
getParameterMetadata().visitRegistrations(
|
||||
new Consumer<QueryParameter>() {
|
||||
int i = 1;
|
||||
|
||||
@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 += registration.getSqlTypes().length;
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw getSession().getJdbcServices().getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Error preparing registered callable parameter",
|
||||
getProcedureName()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
i += parameter.getSqlTypes().length;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return new ProcedureOutputsImpl( this, statement );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw getSession().getJdbcServices().getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Error preparing CallableStatement",
|
||||
getProcedureName()
|
||||
);
|
||||
}
|
||||
return new ProcedureOutputsImpl( this, statement );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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 ) {
|
||||
copy.add( ProcedureCallMementoImpl.ParameterMemento.fromRegistration( registration ) );
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -162,7 +162,7 @@ public class ProcedureCallMementoImpl implements ProcedureCallMemento {
|
|||
registration.getPosition(),
|
||||
registration.getName(),
|
||||
registration.getMode(),
|
||||
registration.getType(),
|
||||
registration.getParameterType(),
|
||||
registration.getHibernateType(),
|
||||
registration.isPassNullsEnabled()
|
||||
);
|
||||
|
|
|
@ -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);;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ) {
|
||||
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() ) ) {
|
||||
public <T> ProcedureParameterImplementor<T> resolve(Parameter<T> param) {
|
||||
if ( ProcedureParameterImplementor.class.isInstance( param ) ) {
|
||||
for ( ProcedureParameterImplementor parameter : parameters ) {
|
||||
if ( parameter == param ) {
|
||||
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 );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in New Issue