Cleanup stored procedure handling and add support for stored procedure function return
This commit is contained in:
parent
1aefd1977a
commit
fb8186d3e8
|
@ -43,6 +43,11 @@ public class QueryHints {
|
|||
*/
|
||||
public static final String CACHEABLE = "org.hibernate.cacheable";
|
||||
|
||||
/**
|
||||
* Is the procedure a function? Note: only valid for named stored procedures.
|
||||
*/
|
||||
public static final String CALLABLE_FUNCTION = "org.hibernate.callableFunction";
|
||||
|
||||
/**
|
||||
* Defines a comment to be applied to the SQL sent to the database.
|
||||
*
|
||||
|
|
|
@ -7,57 +7,29 @@
|
|||
package org.hibernate.procedure.internal;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
||||
import org.hibernate.procedure.spi.ProcedureCallImplementor;
|
||||
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
||||
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
import org.hibernate.sql.exec.spi.JdbcCall;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
|
||||
public abstract class AbstractStandardCallableStatementSupport implements CallableStatementSupport {
|
||||
|
||||
@Override
|
||||
public void registerParameters(
|
||||
String procedureName,
|
||||
ProcedureCallImplementor procedureCall,
|
||||
JdbcCall procedureCall,
|
||||
CallableStatement statement,
|
||||
ParameterStrategy parameterStrategy,
|
||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
||||
SharedSessionContractImplementor session) {
|
||||
|
||||
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
|
||||
final RefCursorSupport refCursorSupport = procedureCall.getSession().getFactory().getServiceRegistry()
|
||||
.getService( RefCursorSupport.class );
|
||||
try {
|
||||
for ( int i = 0; i < registrations.size(); i++ ) {
|
||||
final ProcedureParameterImplementor<?> parameter = registrations.get( i );
|
||||
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
|
||||
if ( procedureCall.getParameterStrategy() == ParameterStrategy.NAMED ) {
|
||||
refCursorSupport
|
||||
.registerRefCursorParameter( statement, parameter.getName() );
|
||||
}
|
||||
else {
|
||||
refCursorSupport
|
||||
.registerRefCursorParameter( statement, parameter.getPosition() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
parameter.prepare( statement, i + 1, procedureCall );
|
||||
}
|
||||
}
|
||||
if ( procedureCall.getFunctionReturn() != null ) {
|
||||
procedureCall.getFunctionReturn().registerParameter( statement, session );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Error registering CallableStatement parameters",
|
||||
procedureName
|
||||
);
|
||||
for ( JdbcCallParameterRegistration parameterRegistration : procedureCall.getParameterRegistrations() ) {
|
||||
parameterRegistration.registerParameter( statement, session );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,7 @@
|
|||
|
||||
package org.hibernate.procedure.internal;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
@ -27,51 +24,52 @@ import org.hibernate.type.descriptor.java.BasicJavaType;
|
|||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class FunctionReturnImpl implements FunctionReturnImplementor {
|
||||
private final ProcedureCallImplementor procedureCall;
|
||||
private int jdbcTypeCode;
|
||||
public class FunctionReturnImpl<T> implements FunctionReturnImplementor<T> {
|
||||
private final ProcedureCallImplementor<T> procedureCall;
|
||||
private final int jdbcTypeCode;
|
||||
|
||||
private AllowableOutputParameterType<?> ormType;
|
||||
private AllowableOutputParameterType<T> ormType;
|
||||
|
||||
public FunctionReturnImpl(ProcedureCallImplementor procedureCall, int jdbcTypeCode) {
|
||||
public FunctionReturnImpl(ProcedureCallImplementor<T> procedureCall, int jdbcTypeCode) {
|
||||
this.procedureCall = procedureCall;
|
||||
this.jdbcTypeCode = jdbcTypeCode;
|
||||
}
|
||||
|
||||
public FunctionReturnImpl(ProcedureCallImplementor procedureCall, AllowableOutputParameterType ormType) {
|
||||
public FunctionReturnImpl(ProcedureCallImplementor<T> procedureCall, AllowableOutputParameterType<T> ormType) {
|
||||
this.procedureCall = procedureCall;
|
||||
this.jdbcTypeCode = ormType.getJdbcTypeDescriptor().getJdbcTypeCode();
|
||||
|
||||
this.ormType = ormType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JdbcCallFunctionReturn toJdbcFunctionReturn(SharedSessionContractImplementor persistenceContext) {
|
||||
final AllowableParameterType ormType;
|
||||
final AllowableParameterType<T> ormType;
|
||||
final JdbcCallRefCursorExtractorImpl refCursorExtractor;
|
||||
final JdbcCallParameterExtractorImpl parameterExtractor;
|
||||
final JdbcCallParameterExtractorImpl<T> parameterExtractor;
|
||||
|
||||
if ( getJdbcTypeCode() == Types.REF_CURSOR ) {
|
||||
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( null, 0 );
|
||||
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( null, 1 );
|
||||
ormType = null;
|
||||
parameterExtractor = null;
|
||||
}
|
||||
else {
|
||||
|
||||
final TypeConfiguration typeConfiguration = persistenceContext.getFactory().getMetamodel().getTypeConfiguration();
|
||||
final JdbcType sqlTypeDescriptor = typeConfiguration.getJdbcTypeDescriptorRegistry()
|
||||
.getDescriptor( getJdbcTypeCode() );
|
||||
final BasicJavaType<?> javaTypeMapping = sqlTypeDescriptor
|
||||
.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration );
|
||||
ormType = typeConfiguration.standardBasicTypeForJavaType( javaTypeMapping.getJavaTypeClass() );
|
||||
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), null, 0, ormType );
|
||||
//noinspection unchecked
|
||||
ormType = (AllowableParameterType<T>) typeConfiguration.standardBasicTypeForJavaType( javaTypeMapping.getJavaTypeClass() );
|
||||
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), null, 1, ormType );
|
||||
refCursorExtractor = null;
|
||||
}
|
||||
|
||||
return new JdbcCallFunctionReturnImpl( getJdbcTypeCode(), ormType, parameterExtractor, refCursorExtractor );
|
||||
return new JdbcCallFunctionReturnImpl( ormType, parameterExtractor, refCursorExtractor );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,7 +78,7 @@ public class FunctionReturnImpl implements FunctionReturnImplementor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public AllowableParameterType getHibernateType() {
|
||||
public AllowableParameterType<T> getHibernateType() {
|
||||
return ormType;
|
||||
}
|
||||
|
||||
|
@ -91,7 +89,7 @@ public class FunctionReturnImpl implements FunctionReturnImplementor {
|
|||
|
||||
@Override
|
||||
public Integer getPosition() {
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -125,17 +123,11 @@ public class FunctionReturnImpl implements FunctionReturnImplementor {
|
|||
public NamedCallableQueryMemento.ParameterMemento toMemento() {
|
||||
return session -> {
|
||||
if ( ormType != null ) {
|
||||
return new FunctionReturnImpl( procedureCall, ormType );
|
||||
return new FunctionReturnImpl<>( procedureCall, ormType );
|
||||
}
|
||||
else {
|
||||
return new FunctionReturnImpl( procedureCall, jdbcTypeCode );
|
||||
return new FunctionReturnImpl<>( procedureCall, jdbcTypeCode );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(CallableStatement statement, int startIndex, ProcedureCallImplementor callImplementor)
|
||||
throws SQLException {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ import java.util.List;
|
|||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.procedure.spi.FunctionReturnImplementor;
|
||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
||||
import org.hibernate.procedure.spi.ProcedureCallImplementor;
|
||||
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
||||
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
||||
import org.hibernate.sql.exec.internal.JdbcCallImpl;
|
||||
|
@ -28,12 +30,11 @@ public class PostgresCallableStatementSupport extends AbstractStandardCallableSt
|
|||
public static final PostgresCallableStatementSupport INSTANCE = new PostgresCallableStatementSupport();
|
||||
|
||||
@Override
|
||||
public JdbcCall interpretCall(
|
||||
String procedureName,
|
||||
FunctionReturnImpl functionReturn,
|
||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
||||
ProcedureParamBindings paramBindings,
|
||||
SharedSessionContractImplementor session) {
|
||||
public JdbcCall interpretCall(ProcedureCallImplementor<?> procedureCall) {
|
||||
final String procedureName = procedureCall.getProcedureName();
|
||||
final FunctionReturnImplementor functionReturn = procedureCall.getFunctionReturn();
|
||||
final ProcedureParameterMetadataImplementor parameterMetadata = procedureCall.getParameterMetadata();
|
||||
final SharedSessionContractImplementor session = procedureCall.getSession();
|
||||
final boolean firstParamIsRefCursor = parameterMetadata.getParameterCount() != 0
|
||||
&& isFirstParameterModeRefCursor( parameterMetadata );
|
||||
|
||||
|
@ -45,42 +46,50 @@ public class PostgresCallableStatementSupport extends AbstractStandardCallableSt
|
|||
}
|
||||
|
||||
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
|
||||
final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder(
|
||||
parameterMetadata.hasNamedParameters() ?
|
||||
ParameterStrategy.NAMED :
|
||||
ParameterStrategy.POSITIONAL
|
||||
);
|
||||
|
||||
final StringBuilder buffer;
|
||||
if ( firstParamIsRefCursor ) {
|
||||
buffer = new StringBuilder(11 + procedureName.length() + registrations.size() * 2).append( "{?=call " );
|
||||
final int offset;
|
||||
final int startIndex;
|
||||
if ( functionReturn != null ) {
|
||||
offset = 2;
|
||||
startIndex = 0;
|
||||
buffer = new StringBuilder( 11 + procedureName.length() + registrations.size() * 2 ).append( "{?=call " );
|
||||
builder.setFunctionReturn( functionReturn.toJdbcFunctionReturn( session ) );
|
||||
}
|
||||
else if ( firstParamIsRefCursor ) {
|
||||
offset = 1;
|
||||
startIndex = 1;
|
||||
buffer = new StringBuilder( 11 + procedureName.length() + registrations.size() * 2 ).append( "{?=call " );
|
||||
builder.addParameterRegistration( registrations.get( 0 ).toJdbcParameterRegistration( 1, procedureCall ) );
|
||||
}
|
||||
else {
|
||||
buffer = new StringBuilder(9 + procedureName.length() + registrations.size() * 2).append( "{call " );
|
||||
offset = 1;
|
||||
startIndex = 0;
|
||||
buffer = new StringBuilder( 9 + procedureName.length() + registrations.size() * 2 ).append( "{call " );
|
||||
}
|
||||
|
||||
buffer.append( procedureName ).append( "(" );
|
||||
|
||||
// skip the first registration if it was a REF_CURSOR
|
||||
final int startIndex;
|
||||
if ( firstParamIsRefCursor ) {
|
||||
startIndex = 1;
|
||||
}
|
||||
else {
|
||||
startIndex = 0;
|
||||
}
|
||||
String sep = "";
|
||||
for ( int i = startIndex; i < registrations.size(); i++ ) {
|
||||
if ( registrations.get( i ).getMode() == ParameterMode.REF_CURSOR ) {
|
||||
final ProcedureParameterImplementor<?> parameter = registrations.get( i );
|
||||
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
|
||||
throw new HibernateException(
|
||||
"PostgreSQL supports only one REF_CURSOR parameter, but multiple were registered" );
|
||||
}
|
||||
buffer.append( sep ).append( "?" );
|
||||
sep = ",";
|
||||
builder.addParameterRegistration( parameter.toJdbcParameterRegistration( i + offset, procedureCall ) );
|
||||
}
|
||||
|
||||
buffer.append( ")}" );
|
||||
return new JdbcCallImpl.Builder(
|
||||
buffer.toString(),
|
||||
parameterMetadata.hasNamedParameters() ?
|
||||
ParameterStrategy.NAMED :
|
||||
ParameterStrategy.POSITIONAL
|
||||
).buildJdbcCall();
|
||||
builder.setCallableName( buffer.toString() );
|
||||
return builder.buildJdbcCall();
|
||||
}
|
||||
|
||||
private static boolean isFirstParameterModeRefCursor(ProcedureParameterMetadataImplementor parameterMetadata) {
|
||||
|
|
|
@ -7,15 +7,83 @@
|
|||
package org.hibernate.procedure.internal;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.annotations.QueryHints;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.graph.GraphSemantic;
|
||||
import org.hibernate.graph.RootGraph;
|
||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.procedure.NoSuchParameterException;
|
||||
import org.hibernate.procedure.ParameterStrategyException;
|
||||
import org.hibernate.procedure.ProcedureCall;
|
||||
import org.hibernate.procedure.ProcedureOutputs;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.FunctionReturnImplementor;
|
||||
import org.hibernate.procedure.spi.NamedCallableQueryMemento;
|
||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
||||
import org.hibernate.procedure.spi.ProcedureCallImplementor;
|
||||
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.query.QueryParameter;
|
||||
import org.hibernate.query.internal.QueryOptionsImpl;
|
||||
import org.hibernate.query.named.NamedQueryMemento;
|
||||
import org.hibernate.query.procedure.ProcedureParameter;
|
||||
import org.hibernate.query.results.ResultSetMapping;
|
||||
import org.hibernate.query.results.ResultSetMappingImpl;
|
||||
import org.hibernate.query.spi.AbstractQuery;
|
||||
import org.hibernate.query.spi.MutableQueryOptions;
|
||||
import org.hibernate.query.spi.QueryImplementor;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryOptionsAdapter;
|
||||
import org.hibernate.query.spi.QueryParameterBinding;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
||||
import org.hibernate.result.NoMoreReturnsException;
|
||||
import org.hibernate.result.Output;
|
||||
import org.hibernate.result.ResultSetOutput;
|
||||
import org.hibernate.result.UpdateCountOutput;
|
||||
import org.hibernate.result.spi.ResultContext;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.exec.internal.CallbackImpl;
|
||||
import org.hibernate.sql.exec.internal.JdbcCallRefCursorExtractorImpl;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
|
||||
import org.hibernate.sql.exec.spi.Callback;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcCall;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallRefCursorExtractor;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.results.NoMoreOutputsException;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.BasicTypeReference;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import jakarta.persistence.FlushModeType;
|
||||
import jakarta.persistence.LockModeType;
|
||||
import jakarta.persistence.NoResultException;
|
||||
|
@ -26,50 +94,6 @@ import jakarta.persistence.PersistenceException;
|
|||
import jakarta.persistence.TemporalType;
|
||||
import jakarta.persistence.TransactionRequiredException;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.graph.GraphSemantic;
|
||||
import org.hibernate.graph.RootGraph;
|
||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.procedure.NoSuchParameterException;
|
||||
import org.hibernate.procedure.ParameterStrategyException;
|
||||
import org.hibernate.procedure.ProcedureCall;
|
||||
import org.hibernate.procedure.ProcedureOutputs;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.NamedCallableQueryMemento;
|
||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
||||
import org.hibernate.procedure.spi.ProcedureCallImplementor;
|
||||
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.query.QueryParameter;
|
||||
import org.hibernate.query.internal.QueryOptionsImpl;
|
||||
import org.hibernate.query.procedure.ProcedureParameter;
|
||||
import org.hibernate.query.results.ResultSetMapping;
|
||||
import org.hibernate.query.results.ResultSetMappingImpl;
|
||||
import org.hibernate.query.spi.AbstractQuery;
|
||||
import org.hibernate.query.spi.MutableQueryOptions;
|
||||
import org.hibernate.query.spi.QueryImplementor;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
||||
import org.hibernate.result.NoMoreReturnsException;
|
||||
import org.hibernate.result.Output;
|
||||
import org.hibernate.result.ResultSetOutput;
|
||||
import org.hibernate.result.UpdateCountOutput;
|
||||
import org.hibernate.result.spi.ResultContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcCall;
|
||||
import org.hibernate.sql.results.NoMoreOutputsException;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.BasicTypeReference;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Standard implementation of {@link ProcedureCall}
|
||||
*
|
||||
|
@ -82,7 +106,7 @@ public class ProcedureCallImpl<R>
|
|||
|
||||
private final String procedureName;
|
||||
|
||||
private FunctionReturnImpl functionReturn;
|
||||
private FunctionReturnImpl<R> functionReturn;
|
||||
|
||||
private final ProcedureParameterMetadataImpl parameterMetadata;
|
||||
private final ProcedureParamBindings paramBindings;
|
||||
|
@ -93,6 +117,7 @@ public class ProcedureCallImpl<R>
|
|||
|
||||
private final QueryOptionsImpl queryOptions = new QueryOptionsImpl();
|
||||
|
||||
private JdbcCall call;
|
||||
private ProcedureOutputsImpl outputs;
|
||||
|
||||
|
||||
|
@ -263,6 +288,30 @@ public class ProcedureCallImpl<R>
|
|||
synchronizedQuerySpaces::add,
|
||||
() -> getSession().getFactory()
|
||||
);
|
||||
|
||||
applyOptions( memento );
|
||||
}
|
||||
|
||||
protected void applyOptions(NamedCallableQueryMemento memento) {
|
||||
applyOptions( (NamedQueryMemento) memento );
|
||||
|
||||
if ( memento.getHints() != null ) {
|
||||
final Object callableFunction = memento.getHints().get( QueryHints.CALLABLE_FUNCTION );
|
||||
if ( callableFunction != null && Boolean.parseBoolean( callableFunction.toString() ) ) {
|
||||
final List<Class<?>> resultTypes = new ArrayList<>();
|
||||
resultSetMapping.visitResultBuilders(
|
||||
(index, resultBuilder) -> resultTypes.add( resultBuilder.getJavaType() )
|
||||
);
|
||||
final TypeConfiguration typeConfiguration = getSessionFactory().getTypeConfiguration();
|
||||
final BasicType<?> type;
|
||||
if ( resultTypes.size() != 1 || ( type = typeConfiguration.getBasicTypeForJavaType( resultTypes.get( 0 ) ) ) == null ) {
|
||||
markAsFunctionCall( Types.REF_CURSOR );
|
||||
}
|
||||
else {
|
||||
markAsFunctionCall( type.getJdbcTypeDescriptor().getJdbcTypeCode() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -299,9 +348,14 @@ public class ProcedureCallImpl<R>
|
|||
return functionReturn != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionReturnImplementor<R> getFunctionReturn() {
|
||||
return functionReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcedureCall markAsFunctionCall(int sqlType) {
|
||||
functionReturn = new FunctionReturnImpl( this, sqlType );
|
||||
functionReturn = new FunctionReturnImpl<>( this, sqlType );
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -519,15 +573,27 @@ public class ProcedureCallImpl<R>
|
|||
.getJdbcEnvironment()
|
||||
.getDialect()
|
||||
.getCallableStatementSupport();
|
||||
final ProcedureParameterMetadataImpl parameterMetadata = getParameterMetadata();
|
||||
final JdbcCall call = callableStatementSupport.interpretCall(
|
||||
procedureName,
|
||||
functionReturn,
|
||||
parameterMetadata,
|
||||
paramBindings,
|
||||
getSession()
|
||||
);
|
||||
this.call = callableStatementSupport.interpretCall( this );
|
||||
|
||||
final Map<ProcedureParameter<?>, JdbcCallParameterRegistration> parameterRegistrations = new IdentityHashMap<>();
|
||||
final List<JdbcCallRefCursorExtractor> refCursorExtractors = new ArrayList<>();
|
||||
if ( functionReturn != null ) {
|
||||
parameterRegistrations.put( functionReturn, call.getFunctionReturn() );
|
||||
final JdbcCallRefCursorExtractorImpl refCursorExtractor = call.getFunctionReturn().getRefCursorExtractor();
|
||||
if ( refCursorExtractor != null ) {
|
||||
refCursorExtractors.add( refCursorExtractor );
|
||||
}
|
||||
}
|
||||
final List<? extends ProcedureParameterImplementor<?>> registrations = getParameterMetadata().getRegistrationsAsList();
|
||||
final List<JdbcCallParameterRegistration> jdbcParameters = call.getParameterRegistrations();
|
||||
for ( int i = 0; i < registrations.size(); i++ ) {
|
||||
final JdbcCallParameterRegistration jdbcCallParameterRegistration = jdbcParameters.get( i );
|
||||
parameterRegistrations.put( registrations.get( i ), jdbcCallParameterRegistration );
|
||||
final JdbcCallRefCursorExtractorImpl refCursorExtractor = jdbcCallParameterRegistration.getRefCursorExtractor();
|
||||
if ( refCursorExtractor != null ) {
|
||||
refCursorExtractors.add( refCursorExtractor );
|
||||
}
|
||||
}
|
||||
|
||||
LOG.debugf( "Preparing procedure call : %s", call );
|
||||
final CallableStatement statement = (CallableStatement) getSession()
|
||||
|
@ -535,35 +601,100 @@ public class ProcedureCallImpl<R>
|
|||
.getStatementPreparer()
|
||||
.prepareStatement( call.getSql(), true );
|
||||
|
||||
callableStatementSupport.registerParameters( procedureName, this,statement, parameterMetadata.getParameterStrategy(), parameterMetadata, getSession() );
|
||||
// getParameterMetadata().visitRegistrations(
|
||||
// new Consumer<QueryParameter<?>>() {
|
||||
// int i = 1;
|
||||
//
|
||||
// @Override
|
||||
// public void accept(QueryParameter queryParameter) {
|
||||
// try {
|
||||
// final ProcedureParameterImplementor registration = (ProcedureParameterImplementor) queryParameter;
|
||||
// registration.prepare( statement, i, ProcedureCallImpl.this );
|
||||
//// if ( registration.getMode() == ParameterMode.REF_CURSOR ) {
|
||||
// i++;
|
||||
//// }
|
||||
//// else {
|
||||
//// i += registration.getHibernateType().getSqlTypes().length;
|
||||
//// }
|
||||
// }
|
||||
// catch (SQLException e) {
|
||||
// throw getSession().getJdbcServices().getSqlExceptionHelper().convert(
|
||||
// e,
|
||||
// "Error preparing registered callable parameter",
|
||||
// getProcedureName()
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// Register the parameter mode and type
|
||||
callableStatementSupport.registerParameters(
|
||||
procedureName,
|
||||
call,
|
||||
statement,
|
||||
parameterMetadata.getParameterStrategy(),
|
||||
parameterMetadata,
|
||||
getSession()
|
||||
);
|
||||
|
||||
return new ProcedureOutputsImpl( this, statement );
|
||||
// Apply the parameter bindings
|
||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterRegistrations.size() );
|
||||
for ( Map.Entry<ProcedureParameter<?>, JdbcCallParameterRegistration> entry : parameterRegistrations.entrySet() ) {
|
||||
final JdbcCallParameterRegistration registration = entry.getValue();
|
||||
if ( registration.getParameterBinder() != null ) {
|
||||
final ProcedureParameter<?> parameter = entry.getKey();
|
||||
final QueryParameterBinding<?> binding = getParameterBindings().getBinding( parameter );
|
||||
if ( !binding.isBound() ) {
|
||||
if ( parameter.getPosition() == null ) {
|
||||
throw new IllegalArgumentException( "The parameter named [" + parameter + "] was not set! You need to call the setParameter method." );
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException( "The parameter at position [" + parameter + "] was not set! You need to call the setParameter method." );
|
||||
}
|
||||
}
|
||||
jdbcParameterBindings.addBinding(
|
||||
(JdbcParameter) registration.getParameterBinder(),
|
||||
new JdbcParameterBindingImpl(
|
||||
(JdbcMapping) registration.getParameterType(),
|
||||
binding.getBindValue()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final JdbcCallRefCursorExtractor[] extractors = refCursorExtractors.toArray( new JdbcCallRefCursorExtractor[0] );
|
||||
|
||||
final ExecutionContext executionContext = new ExecutionContext() {
|
||||
private final Callback callback = new CallbackImpl();
|
||||
|
||||
@Override
|
||||
public SharedSessionContractImplementor getSession() {
|
||||
return ProcedureCallImpl.this.getSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions getQueryOptions() {
|
||||
return new QueryOptionsAdapter() {
|
||||
@Override
|
||||
public Boolean isReadOnly() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryIdentifier(String sql) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameterBindings getQueryParameterBindings() {
|
||||
return QueryParameterBindings.NO_PARAM_BINDINGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Callback getCallback() {
|
||||
return callback;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Note that this should actually happen in an executor
|
||||
|
||||
try {
|
||||
int paramBindingPosition = functionReturn == null ? 1 : 2;
|
||||
for ( JdbcParameterBinder parameterBinder : call.getParameterBinders() ) {
|
||||
parameterBinder.bindParameterValue(
|
||||
statement,
|
||||
paramBindingPosition,
|
||||
jdbcParameterBindings,
|
||||
executionContext
|
||||
);
|
||||
paramBindingPosition++;
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw getSession().getJdbcServices().getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Error registering CallableStatement parameters",
|
||||
procedureName
|
||||
);
|
||||
}
|
||||
return new ProcedureOutputsImpl( this, parameterRegistrations, extractors, statement );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -618,25 +749,6 @@ public class ProcedureCallImpl<R>
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects any parameter registrations which indicate a REF_CURSOR parameter type/mode.
|
||||
*
|
||||
* @return The collected REF_CURSOR type parameters.
|
||||
*/
|
||||
public ProcedureParameterImplementor[] collectRefCursorParameters() {
|
||||
final List<ProcedureParameterImplementor> refCursorParams = new ArrayList<>();
|
||||
|
||||
getParameterMetadata().visitRegistrations(
|
||||
queryParameter -> {
|
||||
final ProcedureParameterImplementor registration = (ProcedureParameterImplementor) queryParameter;
|
||||
if ( registration.getMode() == ParameterMode.REF_CURSOR ) {
|
||||
refCursorParams.add( registration );
|
||||
}
|
||||
}
|
||||
);
|
||||
return refCursorParams.toArray( new ProcedureParameterImplementor[0] );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedCallableQueryMemento toMemento(String name) {
|
||||
return new NamedCallableQueryMementoImpl(
|
||||
|
|
|
@ -8,19 +8,16 @@ package org.hibernate.procedure.internal;
|
|||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
|
||||
import org.hibernate.metamodel.model.domain.AllowableOutputParameterType;
|
||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||
import org.hibernate.procedure.ParameterMisuseException;
|
||||
import org.hibernate.procedure.ProcedureOutputs;
|
||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
||||
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
||||
import org.hibernate.query.procedure.ProcedureParameter;
|
||||
import org.hibernate.result.Output;
|
||||
import org.hibernate.result.internal.OutputsImpl;
|
||||
import org.hibernate.sql.exec.ExecutionException;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallRefCursorExtractor;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
|
@ -33,60 +30,51 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
|
|||
private final ProcedureCallImpl procedureCall;
|
||||
private final CallableStatement callableStatement;
|
||||
|
||||
private final ProcedureParameterImplementor[] refCursorParameters;
|
||||
private final Map<ProcedureParameter<?>, JdbcCallParameterRegistration> parameterRegistrations;
|
||||
private final JdbcCallRefCursorExtractor[] refCursorParameters;
|
||||
private int refCursorParamIndex;
|
||||
|
||||
ProcedureOutputsImpl(ProcedureCallImpl procedureCall, CallableStatement callableStatement) {
|
||||
ProcedureOutputsImpl(
|
||||
ProcedureCallImpl procedureCall,
|
||||
Map<ProcedureParameter<?>, JdbcCallParameterRegistration> parameterRegistrations,
|
||||
JdbcCallRefCursorExtractor[] refCursorParameters,
|
||||
CallableStatement callableStatement) {
|
||||
super( procedureCall, callableStatement );
|
||||
this.procedureCall = procedureCall;
|
||||
this.callableStatement = callableStatement;
|
||||
|
||||
this.refCursorParameters = procedureCall.collectRefCursorParameters();
|
||||
this.parameterRegistrations = parameterRegistrations;
|
||||
this.refCursorParameters = refCursorParameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getOutputParameterValue(ProcedureParameter<T> parameter) {
|
||||
final AllowableParameterType<T> hibernateType = parameter.getHibernateType();
|
||||
if ( parameter.getMode() == ParameterMode.IN ) {
|
||||
throw new ParameterMisuseException( "IN parameter not valid for output extraction" );
|
||||
}
|
||||
final JdbcCallParameterRegistration registration = parameterRegistrations.get( parameter );
|
||||
if ( registration == null ) {
|
||||
throw new IllegalArgumentException( "Parameter [" + parameter + "] is not registered with this procedure call" );
|
||||
}
|
||||
try {
|
||||
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
|
||||
if ( parameter.getPosition() != null ) {
|
||||
return (T) callableStatement.getObject( parameter.getPosition() );
|
||||
}
|
||||
else {
|
||||
return (T) callableStatement.getObject( parameter.getName() );
|
||||
}
|
||||
}
|
||||
else if ( hibernateType instanceof AllowableOutputParameterType<?> ) {
|
||||
|
||||
if ( registration.getParameterMode() == ParameterMode.REF_CURSOR ) {
|
||||
//noinspection unchecked
|
||||
if ( parameter.getPosition() != null ) {
|
||||
return (T) ( (AllowableOutputParameterType<?>) hibernateType ).extract(
|
||||
callableStatement,
|
||||
parameter.getPosition(),
|
||||
procedureCall.getSession()
|
||||
);
|
||||
}
|
||||
else {
|
||||
return (T) ( (AllowableOutputParameterType<?>) hibernateType ).extract(
|
||||
callableStatement,
|
||||
parameter.getName(),
|
||||
procedureCall.getSession()
|
||||
);
|
||||
}
|
||||
return (T) registration.getRefCursorExtractor().extractResultSet(
|
||||
callableStatement,
|
||||
procedureCall.getSession()
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw new ParameterMisuseException( "Parameter type cannot extract procedure output parameters" );
|
||||
//noinspection unchecked
|
||||
return (T) registration.getParameterExtractor().extractValue(
|
||||
callableStatement,
|
||||
parameter.getPosition() == null,
|
||||
procedureCall.getSession()
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
catch (Exception e) {
|
||||
throw new ExecutionException(
|
||||
"Error extracting procedure output parameter value ["
|
||||
+ parameter.getPosition() != null ?
|
||||
String.valueOf( parameter.getPosition() ) :
|
||||
parameter.getName() + "]",
|
||||
"Error extracting procedure output parameter value [" + parameter + "]",
|
||||
e
|
||||
);
|
||||
}
|
||||
|
@ -118,7 +106,7 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
|
|||
@Override
|
||||
public boolean indicatesMoreOutputs() {
|
||||
return super.indicatesMoreOutputs()
|
||||
|| ProcedureOutputsImpl.this.refCursorParamIndex < ProcedureOutputsImpl.this.refCursorParameters.length;
|
||||
|| ProcedureOutputsImpl.this.refCursorParamIndex < refCursorParameters.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -128,19 +116,8 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
|
|||
|
||||
@Override
|
||||
protected Output buildExtendedReturn() {
|
||||
ProcedureOutputsImpl.this.refCursorParamIndex++;
|
||||
final ProcedureParameterImplementor refCursorParam = ProcedureOutputsImpl.this.refCursorParameters[refCursorParamIndex];
|
||||
ResultSet resultSet;
|
||||
if ( refCursorParam.getName() != null ) {
|
||||
resultSet = ProcedureOutputsImpl.this.procedureCall.getSession().getFactory().getServiceRegistry()
|
||||
.getService( RefCursorSupport.class )
|
||||
.getResultSet( ProcedureOutputsImpl.this.callableStatement, refCursorParam.getName() );
|
||||
}
|
||||
else {
|
||||
resultSet = ProcedureOutputsImpl.this.procedureCall.getSession().getFactory().getServiceRegistry()
|
||||
.getService( RefCursorSupport.class )
|
||||
.getResultSet( ProcedureOutputsImpl.this.callableStatement, refCursorParam.getPosition() );
|
||||
}
|
||||
final JdbcCallRefCursorExtractor refCursorParam = refCursorParameters[ProcedureOutputsImpl.this.refCursorParamIndex++];
|
||||
final ResultSet resultSet = refCursorParam.extractResultSet( callableStatement, procedureCall.getSession() );
|
||||
return buildResultSetOutput( () -> extractResults( resultSet ) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@
|
|||
package org.hibernate.procedure.internal;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Objects;
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||
import org.hibernate.procedure.spi.NamedCallableQueryMemento;
|
||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
||||
|
@ -22,28 +23,30 @@ import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
|||
import org.hibernate.query.AbstractQueryParameter;
|
||||
import org.hibernate.query.internal.BindingTypeHelper;
|
||||
import org.hibernate.query.spi.QueryParameterBinding;
|
||||
import org.hibernate.sql.exec.internal.JdbcCallParameterExtractorImpl;
|
||||
import org.hibernate.sql.exec.internal.JdbcCallParameterRegistrationImpl;
|
||||
import org.hibernate.sql.exec.internal.JdbcCallRefCursorExtractorImpl;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.ProcedureParameterNamedBinder;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> implements ProcedureParameterImplementor<T> {
|
||||
private static final Logger log = Logger.getLogger( ProcedureParameterImpl.class );
|
||||
|
||||
private final String name;
|
||||
private Integer position;
|
||||
|
||||
private final Integer position;
|
||||
private final ParameterMode mode;
|
||||
|
||||
private final Class<T> javaType;
|
||||
|
||||
|
||||
public ProcedureParameterImpl(
|
||||
String name,
|
||||
ParameterMode mode,
|
||||
|
@ -111,29 +114,9 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
ProcedureParameterImpl<?> that = (ProcedureParameterImpl<?>) o;
|
||||
return Objects.equals( name, that.name ) &&
|
||||
Objects.equals( position, that.position ) &&
|
||||
mode == that.mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( name, position, mode );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(
|
||||
CallableStatement statement,
|
||||
public JdbcCallParameterRegistration toJdbcParameterRegistration(
|
||||
int startIndex,
|
||||
ProcedureCallImplementor<?> procedureCall) throws SQLException {
|
||||
ProcedureCallImplementor<?> procedureCall) {
|
||||
final QueryParameterBinding<T> binding = procedureCall.getParameterBindings().getBinding( this );
|
||||
final TypeConfiguration typeConfiguration = procedureCall.getSession().getFactory().getTypeConfiguration();
|
||||
final AllowableParameterType<T> typeToUse = BindingTypeHelper.INSTANCE.resolveTemporalPrecision(
|
||||
|
@ -144,115 +127,72 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
|||
typeConfiguration
|
||||
);
|
||||
|
||||
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.
|
||||
final JdbcType recommendedJdbcType = typeToUse.getExpressableJavaTypeDescriptor()
|
||||
.getRecommendedJdbcType( typeConfiguration.getCurrentBaseSqlTypeIndicators() );
|
||||
|
||||
if ( procedureCall.getParameterStrategy() == ParameterStrategy.NAMED &&
|
||||
canDoNameParameterBinding( typeToUse, procedureCall ) ) {
|
||||
statement.registerOutParameter( getName(), recommendedJdbcType.getJdbcTypeCode() );
|
||||
}
|
||||
else {
|
||||
// for ( int i = 0; i < sqlTypesToUse.length; i++ ) {
|
||||
if ( position == null ) {
|
||||
position = startIndex;
|
||||
}
|
||||
statement.registerOutParameter( startIndex, recommendedJdbcType.getJdbcTypeCode() );
|
||||
// }
|
||||
}
|
||||
final String name;
|
||||
if ( procedureCall.getParameterStrategy() == ParameterStrategy.NAMED
|
||||
&& canDoNameParameterBinding( typeToUse, procedureCall ) ) {
|
||||
name = this.name;
|
||||
}
|
||||
else {
|
||||
name = null;
|
||||
}
|
||||
|
||||
if ( mode == ParameterMode.INOUT || mode == ParameterMode.IN ) {
|
||||
final ValueBinder<T> binder;
|
||||
final BasicType<T> basicType;
|
||||
if ( typeToUse instanceof BasicType ) {
|
||||
basicType = ( (BasicType<T>) typeToUse );
|
||||
binder = basicType.getJdbcValueBinder();
|
||||
final JdbcParameterBinder parameterBinder;
|
||||
final JdbcCallRefCursorExtractorImpl refCursorExtractor;
|
||||
final JdbcCallParameterExtractorImpl<T> parameterExtractor;
|
||||
|
||||
switch ( mode ) {
|
||||
case REF_CURSOR:
|
||||
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( name, startIndex );
|
||||
parameterBinder = null;
|
||||
parameterExtractor = null;
|
||||
break;
|
||||
case IN:
|
||||
parameterBinder = getParameterBinder( typeToUse, name );
|
||||
parameterExtractor = null;
|
||||
refCursorExtractor = null;
|
||||
break;
|
||||
case INOUT:
|
||||
parameterBinder = getParameterBinder( typeToUse, name );
|
||||
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), name, startIndex, typeToUse );
|
||||
refCursorExtractor = null;
|
||||
break;
|
||||
default:
|
||||
parameterBinder = null;
|
||||
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), name, startIndex, typeToUse );
|
||||
refCursorExtractor = null;
|
||||
break;
|
||||
}
|
||||
|
||||
return new JdbcCallParameterRegistrationImpl( name, startIndex, mode, typeToUse, parameterBinder, parameterExtractor, refCursorExtractor );
|
||||
}
|
||||
|
||||
private JdbcParameterBinder getParameterBinder(AllowableParameterType<T> typeToUse, String name) {
|
||||
if ( typeToUse instanceof BasicType<?> ) {
|
||||
if ( name == null ) {
|
||||
return new JdbcParameterImpl( (BasicType<T>) typeToUse );
|
||||
}
|
||||
else {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
if ( binding == null || binding.getBindValue() == null ) {
|
||||
// the user did not binding 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 binding 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 ( ( binding != null && binding.isBound() ) || isPassNullsEnabled() ) {
|
||||
log.debugf(
|
||||
"Stored procedure [%s] IN/INOUT parameter [%s] not bound and `passNulls` was set to true; binding NULL",
|
||||
procedureCall.getProcedureName(),
|
||||
this
|
||||
);
|
||||
if ( procedureCall.getParameterStrategy() == ParameterStrategy.NAMED
|
||||
&& canDoNameParameterBinding( typeToUse, procedureCall ) ) {
|
||||
//noinspection unchecked
|
||||
( (ProcedureParameterNamedBinder<T>) typeToUse ).nullSafeSet(
|
||||
statement,
|
||||
null,
|
||||
this.getName(),
|
||||
procedureCall.getSession()
|
||||
return new JdbcParameterImpl( (BasicType<T>) typeToUse ) {
|
||||
@Override
|
||||
protected void bindParameterValue(
|
||||
JdbcMapping jdbcMapping,
|
||||
PreparedStatement statement,
|
||||
Object bindValue,
|
||||
int startPosition,
|
||||
ExecutionContext executionContext) throws SQLException {
|
||||
jdbcMapping.getJdbcValueBinder().bind(
|
||||
(CallableStatement) statement,
|
||||
bindValue,
|
||||
name,
|
||||
executionContext.getSession()
|
||||
);
|
||||
}
|
||||
else {
|
||||
if ( position == null ) {
|
||||
position = startIndex;
|
||||
}
|
||||
binder.bind( statement, null, position, procedureCall.getSession() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"The parameter " +
|
||||
( name != null
|
||||
? "named [" + name + "]"
|
||||
: "at position [" + position + "]" )
|
||||
+ " was not set! You need to call the setParameter method." );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( procedureCall.getParameterStrategy() == ParameterStrategy.NAMED
|
||||
&& canDoNameParameterBinding( typeToUse, procedureCall ) ) {
|
||||
//noinspection unchecked
|
||||
( (ProcedureParameterNamedBinder<T>) typeToUse ).nullSafeSet(
|
||||
statement,
|
||||
binding.getBindValue(),
|
||||
this.getName(),
|
||||
procedureCall.getSession()
|
||||
);
|
||||
}
|
||||
else {
|
||||
if ( position == null ) {
|
||||
position = startIndex;
|
||||
}
|
||||
binder.bind( statement, binding.getBindValue(), position, procedureCall.getSession() );
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canDoNameParameterBinding(
|
||||
|
@ -269,4 +209,33 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
|||
&& hibernateType instanceof ProcedureParameterNamedBinder
|
||||
&& ( (ProcedureParameterNamedBinder<?>) hibernateType ).canDoSetting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( name, position, mode );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
ProcedureParameterImpl<?> that = (ProcedureParameterImpl<?>) o;
|
||||
return Objects.equals( name, that.name ) &&
|
||||
Objects.equals( position, that.position ) &&
|
||||
mode == that.mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if ( position == null ) {
|
||||
return name;
|
||||
}
|
||||
else {
|
||||
return position.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ import java.util.List;
|
|||
import org.hibernate.QueryException;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.procedure.spi.FunctionReturnImplementor;
|
||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
||||
import org.hibernate.procedure.spi.ProcedureCallImplementor;
|
||||
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
||||
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
||||
import org.hibernate.sql.exec.internal.JdbcCallImpl;
|
||||
|
@ -42,39 +44,46 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
|
|||
}
|
||||
|
||||
@Override
|
||||
public JdbcCall interpretCall(
|
||||
String procedureName,
|
||||
FunctionReturnImpl functionReturn,
|
||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
||||
ProcedureParamBindings paramBindings,
|
||||
SharedSessionContractImplementor session) {
|
||||
public JdbcCall interpretCall(ProcedureCallImplementor<?> procedureCall) {
|
||||
final String procedureName = procedureCall.getProcedureName();
|
||||
final FunctionReturnImplementor functionReturn = procedureCall.getFunctionReturn();
|
||||
final ProcedureParameterMetadataImplementor parameterMetadata = procedureCall.getParameterMetadata();
|
||||
final SharedSessionContractImplementor session = procedureCall.getSession();
|
||||
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
|
||||
final StringBuilder buffer = new StringBuilder(9 + procedureName.length() + registrations.size() * 2).append( "{call " )
|
||||
.append( procedureName )
|
||||
.append( "(" );
|
||||
final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder(
|
||||
parameterMetadata.hasNamedParameters() ?
|
||||
ParameterStrategy.NAMED :
|
||||
ParameterStrategy.POSITIONAL
|
||||
);
|
||||
final StringBuilder buffer;
|
||||
final int offset;
|
||||
if ( functionReturn != null ) {
|
||||
offset = 2;
|
||||
buffer = new StringBuilder( 11 + procedureName.length() + registrations.size() * 2 ).append( "{?=call " );
|
||||
builder.setFunctionReturn( functionReturn.toJdbcFunctionReturn( session ) );
|
||||
}
|
||||
else {
|
||||
offset = 1;
|
||||
buffer = new StringBuilder( 9 + procedureName.length() + registrations.size() * 2 ).append( "{call " );
|
||||
}
|
||||
|
||||
buffer.append( procedureName ).append( "(" );
|
||||
|
||||
String sep = "";
|
||||
for ( int i = 0; i < registrations.size(); i++ ) {
|
||||
if ( registrations.get( i ).getMode() == ParameterMode.REF_CURSOR ) {
|
||||
final ProcedureParameterImplementor<?> parameter = registrations.get( i );
|
||||
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
|
||||
verifyRefCursorSupport( session.getJdbcServices().getJdbcEnvironment().getDialect() );
|
||||
buffer.append( sep ).append( "?" );
|
||||
sep = ",";
|
||||
}
|
||||
else {
|
||||
buffer.append( sep ).append( "?" );
|
||||
sep = ",";
|
||||
}
|
||||
buffer.append( sep ).append( "?" );
|
||||
sep = ",";
|
||||
builder.addParameterRegistration( parameter.toJdbcParameterRegistration( i + offset, procedureCall ) );
|
||||
}
|
||||
|
||||
buffer.append( ")}" );
|
||||
|
||||
return new JdbcCallImpl.Builder(
|
||||
buffer.toString(),
|
||||
parameterMetadata.hasNamedParameters() ?
|
||||
ParameterStrategy.NAMED :
|
||||
ParameterStrategy.POSITIONAL
|
||||
).buildJdbcCall();
|
||||
|
||||
builder.setCallableName( buffer.toString() );
|
||||
return builder.buildJdbcCall();
|
||||
}
|
||||
|
||||
private void verifyRefCursorSupport(Dialect dialect) {
|
||||
|
|
|
@ -18,15 +18,12 @@ import org.hibernate.query.results.ResultSetMapping;
|
|||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Utilities used to implement procedure call support.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class Util {
|
||||
private static final Logger log = Logger.getLogger( Util.class );
|
||||
|
||||
private Util() {
|
||||
}
|
||||
|
@ -85,8 +82,7 @@ public class Util {
|
|||
}
|
||||
}
|
||||
else {
|
||||
final JavaType<?> basicType = javaTypeRegistry.getDescriptor(
|
||||
resultSetMappingClass );
|
||||
final JavaType<?> basicType = javaTypeRegistry.getDescriptor( resultSetMappingClass );
|
||||
if ( basicType != null ) {
|
||||
resultSetMapping.addResultBuilder( new ScalarDomainResultBuilder<>( basicType ) );
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ package org.hibernate.procedure.spi;
|
|||
import java.sql.CallableStatement;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.procedure.internal.FunctionReturnImpl;
|
||||
import org.hibernate.procedure.internal.ProcedureParamBindings;
|
||||
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
||||
import org.hibernate.sql.exec.spi.JdbcCall;
|
||||
|
||||
|
@ -18,16 +16,11 @@ import org.hibernate.sql.exec.spi.JdbcCall;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface CallableStatementSupport {
|
||||
JdbcCall interpretCall(
|
||||
String procedureName,
|
||||
FunctionReturnImpl functionReturn,
|
||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
||||
ProcedureParamBindings paramBindings,
|
||||
SharedSessionContractImplementor session);
|
||||
JdbcCall interpretCall(ProcedureCallImplementor<?> procedureCall);
|
||||
|
||||
void registerParameters(
|
||||
String procedureName,
|
||||
ProcedureCallImplementor procedureCall,
|
||||
JdbcCall procedureCall,
|
||||
CallableStatement statement,
|
||||
ParameterStrategy parameterStrategy,
|
||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
||||
|
|
|
@ -6,10 +6,21 @@
|
|||
*/
|
||||
package org.hibernate.procedure.spi;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.procedure.FunctionReturn;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallFunctionReturn;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface FunctionReturnImplementor extends FunctionReturn, ProcedureParameterImplementor {
|
||||
public interface FunctionReturnImplementor<T> extends FunctionReturn<T>, ProcedureParameterImplementor<T> {
|
||||
@Override
|
||||
default JdbcCallParameterRegistration toJdbcParameterRegistration(
|
||||
int startIndex,
|
||||
ProcedureCallImplementor<?> procedureCall) {
|
||||
return toJdbcFunctionReturn( procedureCall.getSession() );
|
||||
}
|
||||
|
||||
JdbcCallFunctionReturn toJdbcFunctionReturn(SharedSessionContractImplementor session);
|
||||
}
|
||||
|
|
|
@ -9,15 +9,17 @@ package org.hibernate.procedure.spi;
|
|||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.procedure.ProcedureCall;
|
||||
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
||||
import org.hibernate.query.spi.QueryImplementor;
|
||||
import org.hibernate.type.BasicTypeReference;
|
||||
|
||||
import jakarta.persistence.FlushModeType;
|
||||
import jakarta.persistence.Parameter;
|
||||
import jakarta.persistence.ParameterMode;
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.procedure.ProcedureCall;
|
||||
import org.hibernate.query.spi.QueryImplementor;
|
||||
import org.hibernate.type.BasicTypeReference;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
@ -29,6 +31,11 @@ public interface ProcedureCallImplementor<R> extends ProcedureCall, QueryImpleme
|
|||
|
||||
ParameterStrategy getParameterStrategy();
|
||||
|
||||
FunctionReturnImplementor getFunctionReturn();
|
||||
|
||||
@Override
|
||||
ProcedureParameterMetadataImplementor getParameterMetadata();
|
||||
|
||||
@Override
|
||||
default R getSingleResult() {
|
||||
return uniqueResult();
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
*/
|
||||
package org.hibernate.procedure.spi;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.query.procedure.ProcedureParameter;
|
||||
import org.hibernate.query.spi.QueryParameterImplementor;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
|
||||
/**
|
||||
* SPI extension for ProcedureParameter
|
||||
|
@ -20,14 +18,7 @@ import org.hibernate.query.spi.QueryParameterImplementor;
|
|||
*/
|
||||
@Incubating
|
||||
public interface ProcedureParameterImplementor<T> extends ProcedureParameter<T>, QueryParameterImplementor<T> {
|
||||
/**
|
||||
* Allow the parameter to register itself with the JDBC CallableStatement,
|
||||
* if necessary, as well as perform any other needed preparation for exeuction
|
||||
*
|
||||
* @throws SQLException Indicates a problem with any underlying JDBC calls
|
||||
*/
|
||||
void prepare(
|
||||
CallableStatement statement,
|
||||
int startIndex,
|
||||
ProcedureCallImplementor<?> callImplementor) throws SQLException;
|
||||
|
||||
JdbcCallParameterRegistration toJdbcParameterRegistration(int startIndex, ProcedureCallImplementor<?> procedureCall);
|
||||
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
*/
|
||||
package org.hibernate.query.procedure;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.query.QueryParameter;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
/**
|
||||
* NOTE: Consider this contract (and its sub-contracts) as incubating as we transition to 6.0 and SQM
|
||||
*
|
||||
|
@ -26,32 +26,4 @@ public interface ProcedureParameter<T> extends QueryParameter<T> {
|
|||
*/
|
||||
ParameterMode getMode();
|
||||
|
||||
/**
|
||||
* How will an unbound value be handled in terms of the JDBC parameter?
|
||||
*
|
||||
* @return {@code true} here indicates that NULL should be passed; {@code false} indicates
|
||||
* that it is ignored.
|
||||
*
|
||||
* @deprecated (since 6.0) : Passing null or not is now triggered by whether
|
||||
* setting the parameter was called at all. In other words a distinction is
|
||||
* made between calling `setParameter` passing {@code null} versus not calling
|
||||
* `setParameter` at all. In the first case, we pass along the {@code null}; in
|
||||
* the second we do not pass {@code null}.
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean isPassNullsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls how unbound values for this IN/INOUT parameter registration will be handled prior to
|
||||
* execution.
|
||||
*
|
||||
* @param enabled {@code true} indicates that the NULL should be passed; {@code false} indicates it should not.
|
||||
*
|
||||
* @deprecated (since 6.0) : see {@link #isPassNullsEnabled}
|
||||
*/
|
||||
@Deprecated
|
||||
default void enablePassingNulls(boolean enabled) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.sql.exec.internal;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
|
@ -25,9 +24,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
|||
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
|
@ -80,6 +77,7 @@ public abstract class AbstractJdbcParameter
|
|||
throw new ExecutionException( "JDBC parameter value not bound - " + this );
|
||||
}
|
||||
|
||||
final Object bindValue = binding.getBindValue();
|
||||
JdbcMapping jdbcMapping = binding.getBindType();
|
||||
|
||||
if ( jdbcMapping == null ) {
|
||||
|
@ -88,11 +86,18 @@ public abstract class AbstractJdbcParameter
|
|||
|
||||
// If the parameter type is not known from the context i.e. null or Object, infer it from the bind value
|
||||
if ( jdbcMapping == null || jdbcMapping.getMappedJavaTypeDescriptor().getJavaTypeClass() == Object.class ) {
|
||||
jdbcMapping = guessBindType( executionContext, binding, jdbcMapping );
|
||||
jdbcMapping = guessBindType( executionContext, bindValue, jdbcMapping );
|
||||
}
|
||||
|
||||
final Object bindValue = binding.getBindValue();
|
||||
bindParameterValue( jdbcMapping, statement, bindValue, startPosition, executionContext );
|
||||
}
|
||||
|
||||
protected void bindParameterValue(
|
||||
JdbcMapping jdbcMapping,
|
||||
PreparedStatement statement,
|
||||
Object bindValue,
|
||||
int startPosition,
|
||||
ExecutionContext executionContext) throws SQLException {
|
||||
//noinspection unchecked
|
||||
jdbcMapping.getJdbcValueBinder().bind(
|
||||
statement,
|
||||
|
@ -102,13 +107,14 @@ public abstract class AbstractJdbcParameter
|
|||
);
|
||||
}
|
||||
|
||||
private JdbcMapping guessBindType(ExecutionContext executionContext, JdbcParameterBinding binding, JdbcMapping jdbcMapping) {
|
||||
if ( binding.getBindValue() == null && jdbcMapping != null ) {
|
||||
private JdbcMapping guessBindType(ExecutionContext executionContext, Object bindValue, JdbcMapping jdbcMapping) {
|
||||
if ( bindValue == null && jdbcMapping != null ) {
|
||||
return jdbcMapping;
|
||||
}
|
||||
|
||||
final AllowableParameterType<?> parameterType = executionContext.getSession().getFactory()
|
||||
.resolveParameterBindType( binding.getBindValue() );
|
||||
final AllowableParameterType<?> parameterType = executionContext.getSession()
|
||||
.getFactory()
|
||||
.resolveParameterBindType( bindValue );
|
||||
if ( parameterType instanceof JdbcMapping ) {
|
||||
return (JdbcMapping) parameterType;
|
||||
}
|
||||
|
|
|
@ -6,25 +6,23 @@
|
|||
*/
|
||||
package org.hibernate.sql.exec.internal;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallFunctionReturn;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class JdbcCallFunctionReturnImpl extends JdbcCallParameterRegistrationImpl implements JdbcCallFunctionReturn {
|
||||
public JdbcCallFunctionReturnImpl(
|
||||
int jdbcTypeCode,
|
||||
AllowableParameterType ormType,
|
||||
JdbcCallParameterExtractorImpl parameterExtractor,
|
||||
JdbcCallRefCursorExtractorImpl refCursorExtractor) {
|
||||
super(
|
||||
null,
|
||||
0,
|
||||
ParameterMode.OUT,
|
||||
jdbcTypeCode,
|
||||
1,
|
||||
ParameterMode.REF_CURSOR,
|
||||
ormType,
|
||||
null,
|
||||
parameterExtractor,
|
||||
|
|
|
@ -115,9 +115,9 @@ public class JdbcCallImpl implements JdbcCall {
|
|||
}
|
||||
|
||||
public static class Builder {
|
||||
private final String callableName;
|
||||
private final ParameterStrategy parameterStrategy;
|
||||
|
||||
private String callableName;
|
||||
private JdbcCallFunctionReturn functionReturn;
|
||||
|
||||
private List<JdbcCallParameterRegistration> parameterRegistrations;
|
||||
|
@ -125,8 +125,7 @@ public class JdbcCallImpl implements JdbcCall {
|
|||
private List<JdbcCallParameterExtractor> parameterExtractors;
|
||||
private List<JdbcCallRefCursorExtractor> refCursorExtractors;
|
||||
|
||||
public Builder(String callableName, ParameterStrategy parameterStrategy) {
|
||||
this.callableName = callableName;
|
||||
public Builder(ParameterStrategy parameterStrategy) {
|
||||
this.parameterStrategy = parameterStrategy;
|
||||
}
|
||||
|
||||
|
@ -134,6 +133,10 @@ public class JdbcCallImpl implements JdbcCall {
|
|||
return new JdbcCallImpl( this );
|
||||
}
|
||||
|
||||
public void setCallableName(String callableName) {
|
||||
this.callableName = callableName;
|
||||
}
|
||||
|
||||
public void setFunctionReturn(JdbcCallFunctionReturn functionReturn) {
|
||||
this.functionReturn = functionReturn;
|
||||
}
|
||||
|
@ -149,6 +152,7 @@ public class JdbcCallImpl implements JdbcCall {
|
|||
|
||||
switch ( registration.getParameterMode() ) {
|
||||
case REF_CURSOR: {
|
||||
addParameterBinder( JdbcParameterBinder.NOOP );
|
||||
addRefCursorExtractor( registration.getRefCursorExtractor() );
|
||||
break;
|
||||
}
|
||||
|
@ -162,6 +166,7 @@ public class JdbcCallImpl implements JdbcCall {
|
|||
break;
|
||||
}
|
||||
case OUT: {
|
||||
addParameterBinder( JdbcParameterBinder.NOOP );
|
||||
addParameterExtractor( registration.getParameterExtractor() );
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ import java.sql.CallableStatement;
|
|||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterExtractor;
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@ import org.hibernate.sql.exec.spi.JdbcCallParameterExtractor;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class JdbcCallParameterExtractorImpl<T> implements JdbcCallParameterExtractor {
|
||||
public class JdbcCallParameterExtractorImpl<T> implements JdbcCallParameterExtractor<T> {
|
||||
private final String callableName;
|
||||
private final String parameterName;
|
||||
private final int parameterPosition;
|
||||
|
@ -58,7 +58,7 @@ public class JdbcCallParameterExtractorImpl<T> implements JdbcCallParameterExtra
|
|||
public T extractValue(
|
||||
CallableStatement callableStatement,
|
||||
boolean shouldUseJdbcNamedParameters,
|
||||
ExecutionContext executionContext) {
|
||||
SharedSessionContractImplementor session) {
|
||||
|
||||
final boolean useNamed = shouldUseJdbcNamedParameters
|
||||
&& parameterName != null;
|
||||
|
@ -68,14 +68,14 @@ public class JdbcCallParameterExtractorImpl<T> implements JdbcCallParameterExtra
|
|||
|
||||
try {
|
||||
if ( useNamed ) {
|
||||
return (T) ormType.extract( callableStatement, parameterName, executionContext.getSession() );
|
||||
return (T) ormType.extract( callableStatement, parameterName, session );
|
||||
}
|
||||
else {
|
||||
return (T) ormType.extract( callableStatement, parameterPosition, executionContext.getSession() );
|
||||
return (T) ormType.extract( callableStatement, parameterPosition, session );
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw executionContext.getSession().getJdbcServices().getSqlExceptionHelper().convert(
|
||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Unable to extract OUT/INOUT parameter value"
|
||||
);
|
||||
|
|
|
@ -8,7 +8,6 @@ package org.hibernate.sql.exec.internal;
|
|||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
@ -19,6 +18,8 @@ import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
|||
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
@ -26,7 +27,6 @@ public class JdbcCallParameterRegistrationImpl implements JdbcCallParameterRegis
|
|||
private final String name;
|
||||
private final int jdbcParameterPositionStart;
|
||||
private final ParameterMode parameterMode;
|
||||
private final int jdbcTypeCode;
|
||||
private final AllowableParameterType ormType;
|
||||
private final JdbcParameterBinder parameterBinder;
|
||||
private final JdbcCallParameterExtractorImpl parameterExtractor;
|
||||
|
@ -36,7 +36,6 @@ public class JdbcCallParameterRegistrationImpl implements JdbcCallParameterRegis
|
|||
String name,
|
||||
int jdbcParameterPositionStart,
|
||||
ParameterMode parameterMode,
|
||||
int jdbcTypeCode,
|
||||
AllowableParameterType ormType,
|
||||
JdbcParameterBinder parameterBinder,
|
||||
JdbcCallParameterExtractorImpl parameterExtractor,
|
||||
|
@ -44,7 +43,6 @@ public class JdbcCallParameterRegistrationImpl implements JdbcCallParameterRegis
|
|||
this.name = name;
|
||||
this.jdbcParameterPositionStart = jdbcParameterPositionStart;
|
||||
this.parameterMode = parameterMode;
|
||||
this.jdbcTypeCode = jdbcTypeCode;
|
||||
this.ormType = ormType;
|
||||
this.parameterBinder = parameterBinder;
|
||||
this.parameterExtractor = parameterExtractor;
|
||||
|
|
|
@ -31,7 +31,6 @@ public class JdbcCallRefCursorExtractorImpl implements JdbcCallRefCursorExtracto
|
|||
this.jdbcParameterPosition = jdbcParameterPosition;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResultSet extractResultSet(
|
||||
CallableStatement callableStatement,
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.sql.exec.spi;
|
|||
|
||||
import java.sql.CallableStatement;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.sql.exec.internal.JdbcCallRefCursorExtractorImpl;
|
||||
|
||||
/**
|
||||
|
@ -25,5 +26,5 @@ public interface JdbcCallParameterExtractor<T> {
|
|||
T extractValue(
|
||||
CallableStatement callableStatement,
|
||||
boolean shouldUseJdbcNamedParameters,
|
||||
ExecutionContext executionContext);
|
||||
SharedSessionContractImplementor session);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ import java.sql.SQLException;
|
|||
* @author John O'Hara
|
||||
*/
|
||||
public interface JdbcParameterBinder {
|
||||
|
||||
JdbcParameterBinder NOOP = (statement, startPosition, jdbcParameterBindings, executionContext) -> {};
|
||||
|
||||
/**
|
||||
* Bind the appropriate value in the JDBC statement
|
||||
*/
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.sql.results.jdbc.internal;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
|
|
|
@ -357,7 +357,7 @@ public class MySQLStoredProcedureTest {
|
|||
scope.inTransaction( entityManager -> {
|
||||
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
||||
.createStoredProcedureCall( "sp_is_null" );
|
||||
procedureCall.registerParameter( 1, StandardBasicTypes.STRING, ParameterMode.IN ).enablePassingNulls( true );
|
||||
procedureCall.registerParameter( 1, StandardBasicTypes.STRING, ParameterMode.IN );
|
||||
procedureCall.registerParameter( 2, Boolean.class, ParameterMode.OUT );
|
||||
procedureCall.setParameter( 1, null );
|
||||
|
||||
|
@ -369,7 +369,7 @@ public class MySQLStoredProcedureTest {
|
|||
scope.inTransaction( entityManager -> {
|
||||
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
||||
.createStoredProcedureCall( "sp_is_null" );
|
||||
procedureCall.registerParameter( 1, StandardBasicTypes.STRING, ParameterMode.IN ).enablePassingNulls( true );
|
||||
procedureCall.registerParameter( 1, StandardBasicTypes.STRING, ParameterMode.IN );
|
||||
procedureCall.registerParameter( 2, Boolean.class, ParameterMode.OUT );
|
||||
procedureCall.setParameter( 1, "test" );
|
||||
|
||||
|
|
|
@ -405,6 +405,23 @@ public class OracleStoredProcedureTest {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNamedProcedureCallStoredProcedureRefCursor(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
List<Object[]> postAndComments = entityManager
|
||||
.createNamedStoredProcedureQuery(
|
||||
"fn_person_and_phones_sq" )
|
||||
.setParameter( 1, 1L )
|
||||
.getResultList();
|
||||
Object[] postAndComment = postAndComments.get( 0 );
|
||||
Person person = (Person) postAndComment[0];
|
||||
Phone phone = (Phone) postAndComment[1];
|
||||
assertEquals( 2, postAndComments.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNamedNativeQueryStoredProcedureRefCursorWithJDBC(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
|
|
|
@ -366,7 +366,7 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
|
|||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
||||
.createStoredProcedureCall( "sp_is_null" );
|
||||
procedureCall.registerParameter( 1, StandardBasicTypes.STRING, ParameterMode.IN ).enablePassingNulls( true );
|
||||
procedureCall.registerParameter( 1, StandardBasicTypes.STRING, ParameterMode.IN );
|
||||
procedureCall.registerParameter( 2, Boolean.class, ParameterMode.OUT );
|
||||
procedureCall.setParameter( 1, null );
|
||||
|
||||
|
@ -378,7 +378,7 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
|
|||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
||||
.createStoredProcedureCall( "sp_is_null" );
|
||||
procedureCall.registerParameter( 1, StandardBasicTypes.STRING, ParameterMode.IN ).enablePassingNulls( true );
|
||||
procedureCall.registerParameter( 1, StandardBasicTypes.STRING, ParameterMode.IN );
|
||||
procedureCall.registerParameter( 2, Boolean.class, ParameterMode.OUT );
|
||||
procedureCall.setParameter( 1, "test" );
|
||||
|
||||
|
|
|
@ -407,7 +407,7 @@ public class StoredProcedureParameterTypeTest {
|
|||
scope.inTransaction(
|
||||
session -> {
|
||||
ProcedureCall procedureCall = session.createStoredProcedureCall( "test" );
|
||||
procedureCall.registerParameter( 1, StandardBasicTypes.STRING, ParameterMode.IN ).enablePassingNulls( true );
|
||||
procedureCall.registerParameter( 1, StandardBasicTypes.STRING, ParameterMode.IN );
|
||||
procedureCall.setParameter( 1, null );
|
||||
}
|
||||
);
|
||||
|
|
|
@ -14,6 +14,7 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.NamedStoredProcedureQuery;
|
||||
import jakarta.persistence.ParameterMode;
|
||||
import jakarta.persistence.QueryHint;
|
||||
import jakarta.persistence.StoredProcedureParameter;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
|
@ -23,9 +24,7 @@ import jakarta.persistence.Table;
|
|||
name = "NumValue.getSomeValues",
|
||||
procedureName = "f_test_return_cursor",
|
||||
resultClasses = NumValue.class,
|
||||
parameters = {
|
||||
@StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = ResultSet.class)
|
||||
}
|
||||
hints = @QueryHint(name = "org.hibernate.callableFunction", value = "true")
|
||||
)
|
||||
public class NumValue implements Serializable {
|
||||
@Id
|
||||
|
|
|
@ -16,15 +16,19 @@ import jakarta.persistence.FieldResult;
|
|||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.NamedStoredProcedureQueries;
|
||||
import jakarta.persistence.NamedStoredProcedureQuery;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OrderColumn;
|
||||
import jakarta.persistence.QueryHint;
|
||||
import jakarta.persistence.SqlResultSetMapping;
|
||||
import jakarta.persistence.SqlResultSetMappings;
|
||||
import jakarta.persistence.StoredProcedureParameter;
|
||||
import jakarta.persistence.Temporal;
|
||||
import jakarta.persistence.TemporalType;
|
||||
import jakarta.persistence.Version;
|
||||
|
||||
import org.hibernate.annotations.NamedNativeQuery;
|
||||
import org.hibernate.annotations.QueryHints;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
|
@ -41,6 +45,15 @@ import org.hibernate.annotations.NamedNativeQuery;
|
|||
callable = false,
|
||||
resultSetMapping = "person_with_phones_hana"
|
||||
)
|
||||
@NamedStoredProcedureQuery(
|
||||
name = "fn_person_and_phones_sq",
|
||||
procedureName = "fn_person_and_phones",
|
||||
resultSetMappings = "person_with_phones",
|
||||
parameters = {
|
||||
@StoredProcedureParameter(type = Long.class)
|
||||
},
|
||||
hints = @QueryHint(name = QueryHints.CALLABLE_FUNCTION, value = "true")
|
||||
)
|
||||
@SqlResultSetMappings({
|
||||
@SqlResultSetMapping(
|
||||
name = "person_with_phones",
|
||||
|
|
|
@ -140,7 +140,26 @@ List<Object[]> postAndComments = entityManager.createNamedQuery("fn_person_and_p
|
|||
|
||||
is going to throw an `IllegalArgumentException`.
|
||||
|
||||
The migration code is
|
||||
If you want to retain the named version, you can change the definition to
|
||||
|
||||
```
|
||||
@NamedStoredProcedureQuery(
|
||||
name = "fn_person_and_phones",
|
||||
procedureName = "fn_person_and_phones",
|
||||
resultSetMapping = "person_with_phones",
|
||||
hints = @QueryHint(name = "org.hibernate.callableFunction", value = "true"),
|
||||
parameters = {
|
||||
@StoredProcedureParameter(type = Long.class)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
and call this like
|
||||
```
|
||||
List<Object[]> postAndComments = entityManager.createNamedStoredProcedureQuery( "fn_person_and_phones" ).setParameter( 1, 1L ).getResultList();
|
||||
```
|
||||
|
||||
or not define the stored procedure and use this code
|
||||
```
|
||||
List<Object[]> postAndComments = entityManager.createStoredProcedureQuery( "fn_person_and_phones", "person_with_phones" ).setParameter( 1, 1L ).getResultList();
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue