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";
|
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.
|
* Defines a comment to be applied to the SQL sent to the database.
|
||||||
*
|
*
|
||||||
|
|
|
@ -7,57 +7,29 @@
|
||||||
package org.hibernate.procedure.internal;
|
package org.hibernate.procedure.internal;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
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.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
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.query.spi.ProcedureParameterMetadataImplementor;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcCall;
|
||||||
import jakarta.persistence.ParameterMode;
|
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||||
|
|
||||||
public abstract class AbstractStandardCallableStatementSupport implements CallableStatementSupport {
|
public abstract class AbstractStandardCallableStatementSupport implements CallableStatementSupport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerParameters(
|
public void registerParameters(
|
||||||
String procedureName,
|
String procedureName,
|
||||||
ProcedureCallImplementor procedureCall,
|
JdbcCall procedureCall,
|
||||||
CallableStatement statement,
|
CallableStatement statement,
|
||||||
ParameterStrategy parameterStrategy,
|
ParameterStrategy parameterStrategy,
|
||||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
ProcedureParameterMetadataImplementor parameterMetadata,
|
||||||
SharedSessionContractImplementor session) {
|
SharedSessionContractImplementor session) {
|
||||||
|
if ( procedureCall.getFunctionReturn() != null ) {
|
||||||
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
|
procedureCall.getFunctionReturn().registerParameter( statement, session );
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (SQLException e) {
|
for ( JdbcCallParameterRegistration parameterRegistration : procedureCall.getParameterRegistrations() ) {
|
||||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
parameterRegistration.registerParameter( statement, session );
|
||||||
e,
|
|
||||||
"Error registering CallableStatement parameters",
|
|
||||||
procedureName
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,7 @@
|
||||||
|
|
||||||
package org.hibernate.procedure.internal;
|
package org.hibernate.procedure.internal;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
import jakarta.persistence.ParameterMode;
|
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
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.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import jakarta.persistence.ParameterMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class FunctionReturnImpl implements FunctionReturnImplementor {
|
public class FunctionReturnImpl<T> implements FunctionReturnImplementor<T> {
|
||||||
private final ProcedureCallImplementor procedureCall;
|
private final ProcedureCallImplementor<T> procedureCall;
|
||||||
private int jdbcTypeCode;
|
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.procedureCall = procedureCall;
|
||||||
this.jdbcTypeCode = jdbcTypeCode;
|
this.jdbcTypeCode = jdbcTypeCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionReturnImpl(ProcedureCallImplementor procedureCall, AllowableOutputParameterType ormType) {
|
public FunctionReturnImpl(ProcedureCallImplementor<T> procedureCall, AllowableOutputParameterType<T> ormType) {
|
||||||
this.procedureCall = procedureCall;
|
this.procedureCall = procedureCall;
|
||||||
this.jdbcTypeCode = ormType.getJdbcTypeDescriptor().getJdbcTypeCode();
|
this.jdbcTypeCode = ormType.getJdbcTypeDescriptor().getJdbcTypeCode();
|
||||||
|
|
||||||
this.ormType = ormType;
|
this.ormType = ormType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public JdbcCallFunctionReturn toJdbcFunctionReturn(SharedSessionContractImplementor persistenceContext) {
|
public JdbcCallFunctionReturn toJdbcFunctionReturn(SharedSessionContractImplementor persistenceContext) {
|
||||||
final AllowableParameterType ormType;
|
final AllowableParameterType<T> ormType;
|
||||||
final JdbcCallRefCursorExtractorImpl refCursorExtractor;
|
final JdbcCallRefCursorExtractorImpl refCursorExtractor;
|
||||||
final JdbcCallParameterExtractorImpl parameterExtractor;
|
final JdbcCallParameterExtractorImpl<T> parameterExtractor;
|
||||||
|
|
||||||
if ( getJdbcTypeCode() == Types.REF_CURSOR ) {
|
if ( getJdbcTypeCode() == Types.REF_CURSOR ) {
|
||||||
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( null, 0 );
|
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( null, 1 );
|
||||||
ormType = null;
|
ormType = null;
|
||||||
parameterExtractor = null;
|
parameterExtractor = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
final TypeConfiguration typeConfiguration = persistenceContext.getFactory().getMetamodel().getTypeConfiguration();
|
final TypeConfiguration typeConfiguration = persistenceContext.getFactory().getMetamodel().getTypeConfiguration();
|
||||||
final JdbcType sqlTypeDescriptor = typeConfiguration.getJdbcTypeDescriptorRegistry()
|
final JdbcType sqlTypeDescriptor = typeConfiguration.getJdbcTypeDescriptorRegistry()
|
||||||
.getDescriptor( getJdbcTypeCode() );
|
.getDescriptor( getJdbcTypeCode() );
|
||||||
final BasicJavaType<?> javaTypeMapping = sqlTypeDescriptor
|
final BasicJavaType<?> javaTypeMapping = sqlTypeDescriptor
|
||||||
.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration );
|
.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration );
|
||||||
ormType = typeConfiguration.standardBasicTypeForJavaType( javaTypeMapping.getJavaTypeClass() );
|
//noinspection unchecked
|
||||||
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), null, 0, ormType );
|
ormType = (AllowableParameterType<T>) typeConfiguration.standardBasicTypeForJavaType( javaTypeMapping.getJavaTypeClass() );
|
||||||
|
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), null, 1, ormType );
|
||||||
refCursorExtractor = null;
|
refCursorExtractor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new JdbcCallFunctionReturnImpl( getJdbcTypeCode(), ormType, parameterExtractor, refCursorExtractor );
|
return new JdbcCallFunctionReturnImpl( ormType, parameterExtractor, refCursorExtractor );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -80,7 +78,7 @@ public class FunctionReturnImpl implements FunctionReturnImplementor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AllowableParameterType getHibernateType() {
|
public AllowableParameterType<T> getHibernateType() {
|
||||||
return ormType;
|
return ormType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +89,7 @@ public class FunctionReturnImpl implements FunctionReturnImplementor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getPosition() {
|
public Integer getPosition() {
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -125,17 +123,11 @@ public class FunctionReturnImpl implements FunctionReturnImplementor {
|
||||||
public NamedCallableQueryMemento.ParameterMemento toMemento() {
|
public NamedCallableQueryMemento.ParameterMemento toMemento() {
|
||||||
return session -> {
|
return session -> {
|
||||||
if ( ormType != null ) {
|
if ( ormType != null ) {
|
||||||
return new FunctionReturnImpl( procedureCall, ormType );
|
return new FunctionReturnImpl<>( procedureCall, ormType );
|
||||||
}
|
}
|
||||||
else {
|
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.HibernateException;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.procedure.spi.FunctionReturnImplementor;
|
||||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
import org.hibernate.procedure.spi.ParameterStrategy;
|
||||||
|
import org.hibernate.procedure.spi.ProcedureCallImplementor;
|
||||||
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
||||||
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
||||||
import org.hibernate.sql.exec.internal.JdbcCallImpl;
|
import org.hibernate.sql.exec.internal.JdbcCallImpl;
|
||||||
|
@ -28,12 +30,11 @@ public class PostgresCallableStatementSupport extends AbstractStandardCallableSt
|
||||||
public static final PostgresCallableStatementSupport INSTANCE = new PostgresCallableStatementSupport();
|
public static final PostgresCallableStatementSupport INSTANCE = new PostgresCallableStatementSupport();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JdbcCall interpretCall(
|
public JdbcCall interpretCall(ProcedureCallImplementor<?> procedureCall) {
|
||||||
String procedureName,
|
final String procedureName = procedureCall.getProcedureName();
|
||||||
FunctionReturnImpl functionReturn,
|
final FunctionReturnImplementor functionReturn = procedureCall.getFunctionReturn();
|
||||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
final ProcedureParameterMetadataImplementor parameterMetadata = procedureCall.getParameterMetadata();
|
||||||
ProcedureParamBindings paramBindings,
|
final SharedSessionContractImplementor session = procedureCall.getSession();
|
||||||
SharedSessionContractImplementor session) {
|
|
||||||
final boolean firstParamIsRefCursor = parameterMetadata.getParameterCount() != 0
|
final boolean firstParamIsRefCursor = parameterMetadata.getParameterCount() != 0
|
||||||
&& isFirstParameterModeRefCursor( parameterMetadata );
|
&& isFirstParameterModeRefCursor( parameterMetadata );
|
||||||
|
|
||||||
|
@ -45,42 +46,50 @@ public class PostgresCallableStatementSupport extends AbstractStandardCallableSt
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
|
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
|
||||||
|
final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder(
|
||||||
|
parameterMetadata.hasNamedParameters() ?
|
||||||
|
ParameterStrategy.NAMED :
|
||||||
|
ParameterStrategy.POSITIONAL
|
||||||
|
);
|
||||||
|
|
||||||
final StringBuilder buffer;
|
final StringBuilder buffer;
|
||||||
if ( firstParamIsRefCursor ) {
|
final int offset;
|
||||||
buffer = new StringBuilder(11 + procedureName.length() + registrations.size() * 2).append( "{?=call " );
|
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 {
|
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( "(" );
|
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 = "";
|
String sep = "";
|
||||||
for ( int i = startIndex; i < registrations.size(); i++ ) {
|
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(
|
throw new HibernateException(
|
||||||
"PostgreSQL supports only one REF_CURSOR parameter, but multiple were registered" );
|
"PostgreSQL supports only one REF_CURSOR parameter, but multiple were registered" );
|
||||||
}
|
}
|
||||||
buffer.append( sep ).append( "?" );
|
buffer.append( sep ).append( "?" );
|
||||||
sep = ",";
|
sep = ",";
|
||||||
|
builder.addParameterRegistration( parameter.toJdbcParameterRegistration( i + offset, procedureCall ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.append( ")}" );
|
buffer.append( ")}" );
|
||||||
return new JdbcCallImpl.Builder(
|
builder.setCallableName( buffer.toString() );
|
||||||
buffer.toString(),
|
return builder.buildJdbcCall();
|
||||||
parameterMetadata.hasNamedParameters() ?
|
|
||||||
ParameterStrategy.NAMED :
|
|
||||||
ParameterStrategy.POSITIONAL
|
|
||||||
).buildJdbcCall();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isFirstParameterModeRefCursor(ProcedureParameterMetadataImplementor parameterMetadata) {
|
private static boolean isFirstParameterModeRefCursor(ProcedureParameterMetadataImplementor parameterMetadata) {
|
||||||
|
|
|
@ -7,15 +7,83 @@
|
||||||
package org.hibernate.procedure.internal;
|
package org.hibernate.procedure.internal;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Types;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
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.FlushModeType;
|
||||||
import jakarta.persistence.LockModeType;
|
import jakarta.persistence.LockModeType;
|
||||||
import jakarta.persistence.NoResultException;
|
import jakarta.persistence.NoResultException;
|
||||||
|
@ -26,50 +94,6 @@ import jakarta.persistence.PersistenceException;
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
import jakarta.persistence.TransactionRequiredException;
|
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}
|
* Standard implementation of {@link ProcedureCall}
|
||||||
*
|
*
|
||||||
|
@ -82,7 +106,7 @@ public class ProcedureCallImpl<R>
|
||||||
|
|
||||||
private final String procedureName;
|
private final String procedureName;
|
||||||
|
|
||||||
private FunctionReturnImpl functionReturn;
|
private FunctionReturnImpl<R> functionReturn;
|
||||||
|
|
||||||
private final ProcedureParameterMetadataImpl parameterMetadata;
|
private final ProcedureParameterMetadataImpl parameterMetadata;
|
||||||
private final ProcedureParamBindings paramBindings;
|
private final ProcedureParamBindings paramBindings;
|
||||||
|
@ -93,6 +117,7 @@ public class ProcedureCallImpl<R>
|
||||||
|
|
||||||
private final QueryOptionsImpl queryOptions = new QueryOptionsImpl();
|
private final QueryOptionsImpl queryOptions = new QueryOptionsImpl();
|
||||||
|
|
||||||
|
private JdbcCall call;
|
||||||
private ProcedureOutputsImpl outputs;
|
private ProcedureOutputsImpl outputs;
|
||||||
|
|
||||||
|
|
||||||
|
@ -263,6 +288,30 @@ public class ProcedureCallImpl<R>
|
||||||
synchronizedQuerySpaces::add,
|
synchronizedQuerySpaces::add,
|
||||||
() -> getSession().getFactory()
|
() -> 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
|
@Override
|
||||||
|
@ -299,9 +348,14 @@ public class ProcedureCallImpl<R>
|
||||||
return functionReturn != null;
|
return functionReturn != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionReturnImplementor<R> getFunctionReturn() {
|
||||||
|
return functionReturn;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProcedureCall markAsFunctionCall(int sqlType) {
|
public ProcedureCall markAsFunctionCall(int sqlType) {
|
||||||
functionReturn = new FunctionReturnImpl( this, sqlType );
|
functionReturn = new FunctionReturnImpl<>( this, sqlType );
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,15 +573,27 @@ public class ProcedureCallImpl<R>
|
||||||
.getJdbcEnvironment()
|
.getJdbcEnvironment()
|
||||||
.getDialect()
|
.getDialect()
|
||||||
.getCallableStatementSupport();
|
.getCallableStatementSupport();
|
||||||
final ProcedureParameterMetadataImpl parameterMetadata = getParameterMetadata();
|
this.call = callableStatementSupport.interpretCall( this );
|
||||||
final JdbcCall call = callableStatementSupport.interpretCall(
|
|
||||||
procedureName,
|
|
||||||
functionReturn,
|
|
||||||
parameterMetadata,
|
|
||||||
paramBindings,
|
|
||||||
getSession()
|
|
||||||
);
|
|
||||||
|
|
||||||
|
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 );
|
LOG.debugf( "Preparing procedure call : %s", call );
|
||||||
final CallableStatement statement = (CallableStatement) getSession()
|
final CallableStatement statement = (CallableStatement) getSession()
|
||||||
|
@ -535,35 +601,100 @@ public class ProcedureCallImpl<R>
|
||||||
.getStatementPreparer()
|
.getStatementPreparer()
|
||||||
.prepareStatement( call.getSql(), true );
|
.prepareStatement( call.getSql(), true );
|
||||||
|
|
||||||
callableStatementSupport.registerParameters( procedureName, this,statement, parameterMetadata.getParameterStrategy(), parameterMetadata, getSession() );
|
// Register the parameter mode and type
|
||||||
// getParameterMetadata().visitRegistrations(
|
callableStatementSupport.registerParameters(
|
||||||
// new Consumer<QueryParameter<?>>() {
|
procedureName,
|
||||||
// int i = 1;
|
call,
|
||||||
//
|
statement,
|
||||||
// @Override
|
parameterMetadata.getParameterStrategy(),
|
||||||
// public void accept(QueryParameter queryParameter) {
|
parameterMetadata,
|
||||||
// try {
|
getSession()
|
||||||
// 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()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
|
|
||||||
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
|
@Override
|
||||||
|
@ -618,25 +749,6 @@ public class ProcedureCallImpl<R>
|
||||||
return this;
|
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
|
@Override
|
||||||
public NamedCallableQueryMemento toMemento(String name) {
|
public NamedCallableQueryMemento toMemento(String name) {
|
||||||
return new NamedCallableQueryMementoImpl(
|
return new NamedCallableQueryMementoImpl(
|
||||||
|
|
|
@ -8,19 +8,16 @@ package org.hibernate.procedure.internal;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
import java.sql.CallableStatement;
|
||||||
import java.sql.ResultSet;
|
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.ParameterMisuseException;
|
||||||
import org.hibernate.procedure.ProcedureOutputs;
|
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.query.procedure.ProcedureParameter;
|
||||||
import org.hibernate.result.Output;
|
import org.hibernate.result.Output;
|
||||||
import org.hibernate.result.internal.OutputsImpl;
|
import org.hibernate.result.internal.OutputsImpl;
|
||||||
import org.hibernate.sql.exec.ExecutionException;
|
import org.hibernate.sql.exec.ExecutionException;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcCallRefCursorExtractor;
|
||||||
|
|
||||||
import jakarta.persistence.ParameterMode;
|
import jakarta.persistence.ParameterMode;
|
||||||
|
|
||||||
|
@ -33,60 +30,51 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
|
||||||
private final ProcedureCallImpl procedureCall;
|
private final ProcedureCallImpl procedureCall;
|
||||||
private final CallableStatement callableStatement;
|
private final CallableStatement callableStatement;
|
||||||
|
|
||||||
private final ProcedureParameterImplementor[] refCursorParameters;
|
private final Map<ProcedureParameter<?>, JdbcCallParameterRegistration> parameterRegistrations;
|
||||||
|
private final JdbcCallRefCursorExtractor[] refCursorParameters;
|
||||||
private int refCursorParamIndex;
|
private int refCursorParamIndex;
|
||||||
|
|
||||||
ProcedureOutputsImpl(ProcedureCallImpl procedureCall, CallableStatement callableStatement) {
|
ProcedureOutputsImpl(
|
||||||
|
ProcedureCallImpl procedureCall,
|
||||||
|
Map<ProcedureParameter<?>, JdbcCallParameterRegistration> parameterRegistrations,
|
||||||
|
JdbcCallRefCursorExtractor[] refCursorParameters,
|
||||||
|
CallableStatement callableStatement) {
|
||||||
super( procedureCall, callableStatement );
|
super( procedureCall, callableStatement );
|
||||||
this.procedureCall = procedureCall;
|
this.procedureCall = procedureCall;
|
||||||
this.callableStatement = callableStatement;
|
this.callableStatement = callableStatement;
|
||||||
|
this.parameterRegistrations = parameterRegistrations;
|
||||||
this.refCursorParameters = procedureCall.collectRefCursorParameters();
|
this.refCursorParameters = refCursorParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T getOutputParameterValue(ProcedureParameter<T> parameter) {
|
public <T> T getOutputParameterValue(ProcedureParameter<T> parameter) {
|
||||||
final AllowableParameterType<T> hibernateType = parameter.getHibernateType();
|
|
||||||
if ( parameter.getMode() == ParameterMode.IN ) {
|
if ( parameter.getMode() == ParameterMode.IN ) {
|
||||||
throw new ParameterMisuseException( "IN parameter not valid for output extraction" );
|
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 {
|
try {
|
||||||
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
|
if ( registration.getParameterMode() == 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<?> ) {
|
|
||||||
|
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
if ( parameter.getPosition() != null ) {
|
return (T) registration.getRefCursorExtractor().extractResultSet(
|
||||||
return (T) ( (AllowableOutputParameterType<?>) hibernateType ).extract(
|
callableStatement,
|
||||||
callableStatement,
|
procedureCall.getSession()
|
||||||
parameter.getPosition(),
|
);
|
||||||
procedureCall.getSession()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (T) ( (AllowableOutputParameterType<?>) hibernateType ).extract(
|
|
||||||
callableStatement,
|
|
||||||
parameter.getName(),
|
|
||||||
procedureCall.getSession()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
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(
|
throw new ExecutionException(
|
||||||
"Error extracting procedure output parameter value ["
|
"Error extracting procedure output parameter value [" + parameter + "]",
|
||||||
+ parameter.getPosition() != null ?
|
|
||||||
String.valueOf( parameter.getPosition() ) :
|
|
||||||
parameter.getName() + "]",
|
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -118,7 +106,7 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
|
||||||
@Override
|
@Override
|
||||||
public boolean indicatesMoreOutputs() {
|
public boolean indicatesMoreOutputs() {
|
||||||
return super.indicatesMoreOutputs()
|
return super.indicatesMoreOutputs()
|
||||||
|| ProcedureOutputsImpl.this.refCursorParamIndex < ProcedureOutputsImpl.this.refCursorParameters.length;
|
|| ProcedureOutputsImpl.this.refCursorParamIndex < refCursorParameters.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -128,19 +116,8 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Output buildExtendedReturn() {
|
protected Output buildExtendedReturn() {
|
||||||
ProcedureOutputsImpl.this.refCursorParamIndex++;
|
final JdbcCallRefCursorExtractor refCursorParam = refCursorParameters[ProcedureOutputsImpl.this.refCursorParamIndex++];
|
||||||
final ProcedureParameterImplementor refCursorParam = ProcedureOutputsImpl.this.refCursorParameters[refCursorParamIndex];
|
final ResultSet resultSet = refCursorParam.extractResultSet( callableStatement, procedureCall.getSession() );
|
||||||
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() );
|
|
||||||
}
|
|
||||||
return buildResultSetOutput( () -> extractResults( resultSet ) );
|
return buildResultSetOutput( () -> extractResults( resultSet ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,14 @@
|
||||||
package org.hibernate.procedure.internal;
|
package org.hibernate.procedure.internal;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import jakarta.persistence.ParameterMode;
|
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
|
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
|
||||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||||
import org.hibernate.procedure.spi.NamedCallableQueryMemento;
|
import org.hibernate.procedure.spi.NamedCallableQueryMemento;
|
||||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
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.AbstractQueryParameter;
|
||||||
import org.hibernate.query.internal.BindingTypeHelper;
|
import org.hibernate.query.internal.BindingTypeHelper;
|
||||||
import org.hibernate.query.spi.QueryParameterBinding;
|
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.BasicType;
|
||||||
import org.hibernate.type.ProcedureParameterNamedBinder;
|
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.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import jakarta.persistence.ParameterMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> implements ProcedureParameterImplementor<T> {
|
public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> implements ProcedureParameterImplementor<T> {
|
||||||
private static final Logger log = Logger.getLogger( ProcedureParameterImpl.class );
|
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private Integer position;
|
private final Integer position;
|
||||||
|
|
||||||
private final ParameterMode mode;
|
private final ParameterMode mode;
|
||||||
|
|
||||||
private final Class<T> javaType;
|
private final Class<T> javaType;
|
||||||
|
|
||||||
|
|
||||||
public ProcedureParameterImpl(
|
public ProcedureParameterImpl(
|
||||||
String name,
|
String name,
|
||||||
ParameterMode mode,
|
ParameterMode mode,
|
||||||
|
@ -111,29 +114,9 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public JdbcCallParameterRegistration toJdbcParameterRegistration(
|
||||||
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,
|
|
||||||
int startIndex,
|
int startIndex,
|
||||||
ProcedureCallImplementor<?> procedureCall) throws SQLException {
|
ProcedureCallImplementor<?> procedureCall) {
|
||||||
final QueryParameterBinding<T> binding = procedureCall.getParameterBindings().getBinding( this );
|
final QueryParameterBinding<T> binding = procedureCall.getParameterBindings().getBinding( this );
|
||||||
final TypeConfiguration typeConfiguration = procedureCall.getSession().getFactory().getTypeConfiguration();
|
final TypeConfiguration typeConfiguration = procedureCall.getSession().getFactory().getTypeConfiguration();
|
||||||
final AllowableParameterType<T> typeToUse = BindingTypeHelper.INSTANCE.resolveTemporalPrecision(
|
final AllowableParameterType<T> typeToUse = BindingTypeHelper.INSTANCE.resolveTemporalPrecision(
|
||||||
|
@ -144,115 +127,72 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
||||||
typeConfiguration
|
typeConfiguration
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) {
|
final String name;
|
||||||
|
if ( procedureCall.getParameterStrategy() == ParameterStrategy.NAMED
|
||||||
// if ( sqlTypesToUse.length > 1 ) {
|
&& canDoNameParameterBinding( typeToUse, procedureCall ) ) {
|
||||||
// // there is more than one column involved; see if the Hibernate Type can handle
|
name = this.name;
|
||||||
// // multi-param extraction...
|
}
|
||||||
// final boolean canHandleMultiParamExtraction =
|
else {
|
||||||
// ProcedureParameterExtractionAware.class.isInstance( typeToUse )
|
name = null;
|
||||||
// && ( (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() );
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mode == ParameterMode.INOUT || mode == ParameterMode.IN ) {
|
final JdbcParameterBinder parameterBinder;
|
||||||
final ValueBinder<T> binder;
|
final JdbcCallRefCursorExtractorImpl refCursorExtractor;
|
||||||
final BasicType<T> basicType;
|
final JdbcCallParameterExtractorImpl<T> parameterExtractor;
|
||||||
if ( typeToUse instanceof BasicType ) {
|
|
||||||
basicType = ( (BasicType<T>) typeToUse );
|
switch ( mode ) {
|
||||||
binder = basicType.getJdbcValueBinder();
|
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 {
|
else {
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
return new JdbcParameterImpl( (BasicType<T>) typeToUse ) {
|
||||||
}
|
@Override
|
||||||
if ( binding == null || binding.getBindValue() == null ) {
|
protected void bindParameterValue(
|
||||||
// the user did not binding a value to the parameter being processed. This is the condition
|
JdbcMapping jdbcMapping,
|
||||||
// defined by `passNulls` and that value controls what happens here. If `passNulls` is
|
PreparedStatement statement,
|
||||||
// {@code true} we will binding the NULL value into the statement; if `passNulls` is
|
Object bindValue,
|
||||||
// {@code false} we will not.
|
int startPosition,
|
||||||
//
|
ExecutionContext executionContext) throws SQLException {
|
||||||
// Unfortunately there is not a way to reliably know through JDBC metadata whether a procedure
|
jdbcMapping.getJdbcValueBinder().bind(
|
||||||
// parameter defines a default value. Deferring to that information would be the best option
|
(CallableStatement) statement,
|
||||||
if ( ( binding != null && binding.isBound() ) || isPassNullsEnabled() ) {
|
bindValue,
|
||||||
log.debugf(
|
name,
|
||||||
"Stored procedure [%s] IN/INOUT parameter [%s] not bound and `passNulls` was set to true; binding NULL",
|
executionContext.getSession()
|
||||||
procedureCall.getProcedureName(),
|
|
||||||
this
|
|
||||||
);
|
|
||||||
if ( procedureCall.getParameterStrategy() == ParameterStrategy.NAMED
|
|
||||||
&& canDoNameParameterBinding( typeToUse, procedureCall ) ) {
|
|
||||||
//noinspection unchecked
|
|
||||||
( (ProcedureParameterNamedBinder<T>) typeToUse ).nullSafeSet(
|
|
||||||
statement,
|
|
||||||
null,
|
|
||||||
this.getName(),
|
|
||||||
procedureCall.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(
|
private boolean canDoNameParameterBinding(
|
||||||
|
@ -269,4 +209,33 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
||||||
&& hibernateType instanceof ProcedureParameterNamedBinder
|
&& hibernateType instanceof ProcedureParameterNamedBinder
|
||||||
&& ( (ProcedureParameterNamedBinder<?>) hibernateType ).canDoSetting();
|
&& ( (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.QueryException;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.procedure.spi.FunctionReturnImplementor;
|
||||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
import org.hibernate.procedure.spi.ParameterStrategy;
|
||||||
|
import org.hibernate.procedure.spi.ProcedureCallImplementor;
|
||||||
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
||||||
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
||||||
import org.hibernate.sql.exec.internal.JdbcCallImpl;
|
import org.hibernate.sql.exec.internal.JdbcCallImpl;
|
||||||
|
@ -42,39 +44,46 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JdbcCall interpretCall(
|
public JdbcCall interpretCall(ProcedureCallImplementor<?> procedureCall) {
|
||||||
String procedureName,
|
final String procedureName = procedureCall.getProcedureName();
|
||||||
FunctionReturnImpl functionReturn,
|
final FunctionReturnImplementor functionReturn = procedureCall.getFunctionReturn();
|
||||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
final ProcedureParameterMetadataImplementor parameterMetadata = procedureCall.getParameterMetadata();
|
||||||
ProcedureParamBindings paramBindings,
|
final SharedSessionContractImplementor session = procedureCall.getSession();
|
||||||
SharedSessionContractImplementor session) {
|
|
||||||
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
|
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
|
||||||
final StringBuilder buffer = new StringBuilder(9 + procedureName.length() + registrations.size() * 2).append( "{call " )
|
final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder(
|
||||||
.append( procedureName )
|
parameterMetadata.hasNamedParameters() ?
|
||||||
.append( "(" );
|
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 = "";
|
String sep = "";
|
||||||
for ( int i = 0; i < registrations.size(); i++ ) {
|
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() );
|
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( ")}" );
|
buffer.append( ")}" );
|
||||||
|
|
||||||
return new JdbcCallImpl.Builder(
|
builder.setCallableName( buffer.toString() );
|
||||||
buffer.toString(),
|
return builder.buildJdbcCall();
|
||||||
parameterMetadata.hasNamedParameters() ?
|
|
||||||
ParameterStrategy.NAMED :
|
|
||||||
ParameterStrategy.POSITIONAL
|
|
||||||
).buildJdbcCall();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyRefCursorSupport(Dialect dialect) {
|
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.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utilities used to implement procedure call support.
|
* Utilities used to implement procedure call support.
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class Util {
|
public class Util {
|
||||||
private static final Logger log = Logger.getLogger( Util.class );
|
|
||||||
|
|
||||||
private Util() {
|
private Util() {
|
||||||
}
|
}
|
||||||
|
@ -85,8 +82,7 @@ public class Util {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final JavaType<?> basicType = javaTypeRegistry.getDescriptor(
|
final JavaType<?> basicType = javaTypeRegistry.getDescriptor( resultSetMappingClass );
|
||||||
resultSetMappingClass );
|
|
||||||
if ( basicType != null ) {
|
if ( basicType != null ) {
|
||||||
resultSetMapping.addResultBuilder( new ScalarDomainResultBuilder<>( basicType ) );
|
resultSetMapping.addResultBuilder( new ScalarDomainResultBuilder<>( basicType ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,6 @@ package org.hibernate.procedure.spi;
|
||||||
import java.sql.CallableStatement;
|
import java.sql.CallableStatement;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
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.query.spi.ProcedureParameterMetadataImplementor;
|
||||||
import org.hibernate.sql.exec.spi.JdbcCall;
|
import org.hibernate.sql.exec.spi.JdbcCall;
|
||||||
|
|
||||||
|
@ -18,16 +16,11 @@ import org.hibernate.sql.exec.spi.JdbcCall;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface CallableStatementSupport {
|
public interface CallableStatementSupport {
|
||||||
JdbcCall interpretCall(
|
JdbcCall interpretCall(ProcedureCallImplementor<?> procedureCall);
|
||||||
String procedureName,
|
|
||||||
FunctionReturnImpl functionReturn,
|
|
||||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
|
||||||
ProcedureParamBindings paramBindings,
|
|
||||||
SharedSessionContractImplementor session);
|
|
||||||
|
|
||||||
void registerParameters(
|
void registerParameters(
|
||||||
String procedureName,
|
String procedureName,
|
||||||
ProcedureCallImplementor procedureCall,
|
JdbcCall procedureCall,
|
||||||
CallableStatement statement,
|
CallableStatement statement,
|
||||||
ParameterStrategy parameterStrategy,
|
ParameterStrategy parameterStrategy,
|
||||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
ProcedureParameterMetadataImplementor parameterMetadata,
|
||||||
|
|
|
@ -6,10 +6,21 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.procedure.spi;
|
package org.hibernate.procedure.spi;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.procedure.FunctionReturn;
|
import org.hibernate.procedure.FunctionReturn;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcCallFunctionReturn;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @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.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
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.FlushModeType;
|
||||||
import jakarta.persistence.Parameter;
|
import jakarta.persistence.Parameter;
|
||||||
import jakarta.persistence.ParameterMode;
|
import jakarta.persistence.ParameterMode;
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import org.hibernate.procedure.ProcedureCall;
|
|
||||||
import org.hibernate.query.spi.QueryImplementor;
|
|
||||||
import org.hibernate.type.BasicTypeReference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -29,6 +31,11 @@ public interface ProcedureCallImplementor<R> extends ProcedureCall, QueryImpleme
|
||||||
|
|
||||||
ParameterStrategy getParameterStrategy();
|
ParameterStrategy getParameterStrategy();
|
||||||
|
|
||||||
|
FunctionReturnImplementor getFunctionReturn();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ProcedureParameterMetadataImplementor getParameterMetadata();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default R getSingleResult() {
|
default R getSingleResult() {
|
||||||
return uniqueResult();
|
return uniqueResult();
|
||||||
|
|
|
@ -6,12 +6,10 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.procedure.spi;
|
package org.hibernate.procedure.spi;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.Incubating;
|
import org.hibernate.Incubating;
|
||||||
import org.hibernate.query.procedure.ProcedureParameter;
|
import org.hibernate.query.procedure.ProcedureParameter;
|
||||||
import org.hibernate.query.spi.QueryParameterImplementor;
|
import org.hibernate.query.spi.QueryParameterImplementor;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SPI extension for ProcedureParameter
|
* SPI extension for ProcedureParameter
|
||||||
|
@ -20,14 +18,7 @@ import org.hibernate.query.spi.QueryParameterImplementor;
|
||||||
*/
|
*/
|
||||||
@Incubating
|
@Incubating
|
||||||
public interface ProcedureParameterImplementor<T> extends ProcedureParameter<T>, QueryParameterImplementor<T> {
|
public interface ProcedureParameterImplementor<T> extends ProcedureParameter<T>, QueryParameterImplementor<T> {
|
||||||
/**
|
|
||||||
* Allow the parameter to register itself with the JDBC CallableStatement,
|
JdbcCallParameterRegistration toJdbcParameterRegistration(int startIndex, ProcedureCallImplementor<?> procedureCall);
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.procedure;
|
package org.hibernate.query.procedure;
|
||||||
|
|
||||||
import jakarta.persistence.ParameterMode;
|
|
||||||
|
|
||||||
import org.hibernate.Incubating;
|
import org.hibernate.Incubating;
|
||||||
import org.hibernate.query.QueryParameter;
|
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
|
* 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();
|
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.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.mapping.IndexedConsumer;
|
import org.hibernate.mapping.IndexedConsumer;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
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.JdbcParameterBinding;
|
||||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||||
import org.hibernate.type.BasicType;
|
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,6 +77,7 @@ public abstract class AbstractJdbcParameter
|
||||||
throw new ExecutionException( "JDBC parameter value not bound - " + this );
|
throw new ExecutionException( "JDBC parameter value not bound - " + this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Object bindValue = binding.getBindValue();
|
||||||
JdbcMapping jdbcMapping = binding.getBindType();
|
JdbcMapping jdbcMapping = binding.getBindType();
|
||||||
|
|
||||||
if ( jdbcMapping == null ) {
|
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 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 ) {
|
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
|
//noinspection unchecked
|
||||||
jdbcMapping.getJdbcValueBinder().bind(
|
jdbcMapping.getJdbcValueBinder().bind(
|
||||||
statement,
|
statement,
|
||||||
|
@ -102,13 +107,14 @@ public abstract class AbstractJdbcParameter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JdbcMapping guessBindType(ExecutionContext executionContext, JdbcParameterBinding binding, JdbcMapping jdbcMapping) {
|
private JdbcMapping guessBindType(ExecutionContext executionContext, Object bindValue, JdbcMapping jdbcMapping) {
|
||||||
if ( binding.getBindValue() == null && jdbcMapping != null ) {
|
if ( bindValue == null && jdbcMapping != null ) {
|
||||||
return jdbcMapping;
|
return jdbcMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
final AllowableParameterType<?> parameterType = executionContext.getSession().getFactory()
|
final AllowableParameterType<?> parameterType = executionContext.getSession()
|
||||||
.resolveParameterBindType( binding.getBindValue() );
|
.getFactory()
|
||||||
|
.resolveParameterBindType( bindValue );
|
||||||
if ( parameterType instanceof JdbcMapping ) {
|
if ( parameterType instanceof JdbcMapping ) {
|
||||||
return (JdbcMapping) parameterType;
|
return (JdbcMapping) parameterType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,25 +6,23 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.sql.exec.internal;
|
package org.hibernate.sql.exec.internal;
|
||||||
|
|
||||||
import jakarta.persistence.ParameterMode;
|
|
||||||
|
|
||||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||||
import org.hibernate.sql.exec.spi.JdbcCallFunctionReturn;
|
import org.hibernate.sql.exec.spi.JdbcCallFunctionReturn;
|
||||||
|
|
||||||
|
import jakarta.persistence.ParameterMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class JdbcCallFunctionReturnImpl extends JdbcCallParameterRegistrationImpl implements JdbcCallFunctionReturn {
|
public class JdbcCallFunctionReturnImpl extends JdbcCallParameterRegistrationImpl implements JdbcCallFunctionReturn {
|
||||||
public JdbcCallFunctionReturnImpl(
|
public JdbcCallFunctionReturnImpl(
|
||||||
int jdbcTypeCode,
|
|
||||||
AllowableParameterType ormType,
|
AllowableParameterType ormType,
|
||||||
JdbcCallParameterExtractorImpl parameterExtractor,
|
JdbcCallParameterExtractorImpl parameterExtractor,
|
||||||
JdbcCallRefCursorExtractorImpl refCursorExtractor) {
|
JdbcCallRefCursorExtractorImpl refCursorExtractor) {
|
||||||
super(
|
super(
|
||||||
null,
|
null,
|
||||||
0,
|
1,
|
||||||
ParameterMode.OUT,
|
ParameterMode.REF_CURSOR,
|
||||||
jdbcTypeCode,
|
|
||||||
ormType,
|
ormType,
|
||||||
null,
|
null,
|
||||||
parameterExtractor,
|
parameterExtractor,
|
||||||
|
|
|
@ -115,9 +115,9 @@ public class JdbcCallImpl implements JdbcCall {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private final String callableName;
|
|
||||||
private final ParameterStrategy parameterStrategy;
|
private final ParameterStrategy parameterStrategy;
|
||||||
|
|
||||||
|
private String callableName;
|
||||||
private JdbcCallFunctionReturn functionReturn;
|
private JdbcCallFunctionReturn functionReturn;
|
||||||
|
|
||||||
private List<JdbcCallParameterRegistration> parameterRegistrations;
|
private List<JdbcCallParameterRegistration> parameterRegistrations;
|
||||||
|
@ -125,8 +125,7 @@ public class JdbcCallImpl implements JdbcCall {
|
||||||
private List<JdbcCallParameterExtractor> parameterExtractors;
|
private List<JdbcCallParameterExtractor> parameterExtractors;
|
||||||
private List<JdbcCallRefCursorExtractor> refCursorExtractors;
|
private List<JdbcCallRefCursorExtractor> refCursorExtractors;
|
||||||
|
|
||||||
public Builder(String callableName, ParameterStrategy parameterStrategy) {
|
public Builder(ParameterStrategy parameterStrategy) {
|
||||||
this.callableName = callableName;
|
|
||||||
this.parameterStrategy = parameterStrategy;
|
this.parameterStrategy = parameterStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +133,10 @@ public class JdbcCallImpl implements JdbcCall {
|
||||||
return new JdbcCallImpl( this );
|
return new JdbcCallImpl( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCallableName(String callableName) {
|
||||||
|
this.callableName = callableName;
|
||||||
|
}
|
||||||
|
|
||||||
public void setFunctionReturn(JdbcCallFunctionReturn functionReturn) {
|
public void setFunctionReturn(JdbcCallFunctionReturn functionReturn) {
|
||||||
this.functionReturn = functionReturn;
|
this.functionReturn = functionReturn;
|
||||||
}
|
}
|
||||||
|
@ -149,6 +152,7 @@ public class JdbcCallImpl implements JdbcCall {
|
||||||
|
|
||||||
switch ( registration.getParameterMode() ) {
|
switch ( registration.getParameterMode() ) {
|
||||||
case REF_CURSOR: {
|
case REF_CURSOR: {
|
||||||
|
addParameterBinder( JdbcParameterBinder.NOOP );
|
||||||
addRefCursorExtractor( registration.getRefCursorExtractor() );
|
addRefCursorExtractor( registration.getRefCursorExtractor() );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -162,6 +166,7 @@ public class JdbcCallImpl implements JdbcCall {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OUT: {
|
case OUT: {
|
||||||
|
addParameterBinder( JdbcParameterBinder.NOOP );
|
||||||
addParameterExtractor( registration.getParameterExtractor() );
|
addParameterExtractor( registration.getParameterExtractor() );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,9 @@ import java.sql.CallableStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||||
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
||||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
|
||||||
import org.hibernate.sql.exec.spi.JdbcCallParameterExtractor;
|
import org.hibernate.sql.exec.spi.JdbcCallParameterExtractor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,7 +20,7 @@ import org.hibernate.sql.exec.spi.JdbcCallParameterExtractor;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class JdbcCallParameterExtractorImpl<T> implements JdbcCallParameterExtractor {
|
public class JdbcCallParameterExtractorImpl<T> implements JdbcCallParameterExtractor<T> {
|
||||||
private final String callableName;
|
private final String callableName;
|
||||||
private final String parameterName;
|
private final String parameterName;
|
||||||
private final int parameterPosition;
|
private final int parameterPosition;
|
||||||
|
@ -58,7 +58,7 @@ public class JdbcCallParameterExtractorImpl<T> implements JdbcCallParameterExtra
|
||||||
public T extractValue(
|
public T extractValue(
|
||||||
CallableStatement callableStatement,
|
CallableStatement callableStatement,
|
||||||
boolean shouldUseJdbcNamedParameters,
|
boolean shouldUseJdbcNamedParameters,
|
||||||
ExecutionContext executionContext) {
|
SharedSessionContractImplementor session) {
|
||||||
|
|
||||||
final boolean useNamed = shouldUseJdbcNamedParameters
|
final boolean useNamed = shouldUseJdbcNamedParameters
|
||||||
&& parameterName != null;
|
&& parameterName != null;
|
||||||
|
@ -68,14 +68,14 @@ public class JdbcCallParameterExtractorImpl<T> implements JdbcCallParameterExtra
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ( useNamed ) {
|
if ( useNamed ) {
|
||||||
return (T) ormType.extract( callableStatement, parameterName, executionContext.getSession() );
|
return (T) ormType.extract( callableStatement, parameterName, session );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return (T) ormType.extract( callableStatement, parameterPosition, executionContext.getSession() );
|
return (T) ormType.extract( callableStatement, parameterPosition, session );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (SQLException e) {
|
catch (SQLException e) {
|
||||||
throw executionContext.getSession().getJdbcServices().getSqlExceptionHelper().convert(
|
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
||||||
e,
|
e,
|
||||||
"Unable to extract OUT/INOUT parameter value"
|
"Unable to extract OUT/INOUT parameter value"
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,6 @@ package org.hibernate.sql.exec.internal;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
import java.sql.CallableStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import jakarta.persistence.ParameterMode;
|
|
||||||
|
|
||||||
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
|
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
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.sql.exec.spi.JdbcParameterBinder;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
|
||||||
|
import jakarta.persistence.ParameterMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -26,7 +27,6 @@ public class JdbcCallParameterRegistrationImpl implements JdbcCallParameterRegis
|
||||||
private final String name;
|
private final String name;
|
||||||
private final int jdbcParameterPositionStart;
|
private final int jdbcParameterPositionStart;
|
||||||
private final ParameterMode parameterMode;
|
private final ParameterMode parameterMode;
|
||||||
private final int jdbcTypeCode;
|
|
||||||
private final AllowableParameterType ormType;
|
private final AllowableParameterType ormType;
|
||||||
private final JdbcParameterBinder parameterBinder;
|
private final JdbcParameterBinder parameterBinder;
|
||||||
private final JdbcCallParameterExtractorImpl parameterExtractor;
|
private final JdbcCallParameterExtractorImpl parameterExtractor;
|
||||||
|
@ -36,7 +36,6 @@ public class JdbcCallParameterRegistrationImpl implements JdbcCallParameterRegis
|
||||||
String name,
|
String name,
|
||||||
int jdbcParameterPositionStart,
|
int jdbcParameterPositionStart,
|
||||||
ParameterMode parameterMode,
|
ParameterMode parameterMode,
|
||||||
int jdbcTypeCode,
|
|
||||||
AllowableParameterType ormType,
|
AllowableParameterType ormType,
|
||||||
JdbcParameterBinder parameterBinder,
|
JdbcParameterBinder parameterBinder,
|
||||||
JdbcCallParameterExtractorImpl parameterExtractor,
|
JdbcCallParameterExtractorImpl parameterExtractor,
|
||||||
|
@ -44,7 +43,6 @@ public class JdbcCallParameterRegistrationImpl implements JdbcCallParameterRegis
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.jdbcParameterPositionStart = jdbcParameterPositionStart;
|
this.jdbcParameterPositionStart = jdbcParameterPositionStart;
|
||||||
this.parameterMode = parameterMode;
|
this.parameterMode = parameterMode;
|
||||||
this.jdbcTypeCode = jdbcTypeCode;
|
|
||||||
this.ormType = ormType;
|
this.ormType = ormType;
|
||||||
this.parameterBinder = parameterBinder;
|
this.parameterBinder = parameterBinder;
|
||||||
this.parameterExtractor = parameterExtractor;
|
this.parameterExtractor = parameterExtractor;
|
||||||
|
|
|
@ -31,7 +31,6 @@ public class JdbcCallRefCursorExtractorImpl implements JdbcCallRefCursorExtracto
|
||||||
this.jdbcParameterPosition = jdbcParameterPosition;
|
this.jdbcParameterPosition = jdbcParameterPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResultSet extractResultSet(
|
public ResultSet extractResultSet(
|
||||||
CallableStatement callableStatement,
|
CallableStatement callableStatement,
|
||||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.sql.exec.spi;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
import java.sql.CallableStatement;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.sql.exec.internal.JdbcCallRefCursorExtractorImpl;
|
import org.hibernate.sql.exec.internal.JdbcCallRefCursorExtractorImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,5 +26,5 @@ public interface JdbcCallParameterExtractor<T> {
|
||||||
T extractValue(
|
T extractValue(
|
||||||
CallableStatement callableStatement,
|
CallableStatement callableStatement,
|
||||||
boolean shouldUseJdbcNamedParameters,
|
boolean shouldUseJdbcNamedParameters,
|
||||||
ExecutionContext executionContext);
|
SharedSessionContractImplementor session);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,9 @@ import java.sql.SQLException;
|
||||||
* @author John O'Hara
|
* @author John O'Hara
|
||||||
*/
|
*/
|
||||||
public interface JdbcParameterBinder {
|
public interface JdbcParameterBinder {
|
||||||
|
|
||||||
|
JdbcParameterBinder NOOP = (statement, startPosition, jdbcParameterBindings, executionContext) -> {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind the appropriate value in the JDBC statement
|
* 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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
|
|
@ -357,7 +357,7 @@ public class MySQLStoredProcedureTest {
|
||||||
scope.inTransaction( entityManager -> {
|
scope.inTransaction( entityManager -> {
|
||||||
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
||||||
.createStoredProcedureCall( "sp_is_null" );
|
.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.registerParameter( 2, Boolean.class, ParameterMode.OUT );
|
||||||
procedureCall.setParameter( 1, null );
|
procedureCall.setParameter( 1, null );
|
||||||
|
|
||||||
|
@ -369,7 +369,7 @@ public class MySQLStoredProcedureTest {
|
||||||
scope.inTransaction( entityManager -> {
|
scope.inTransaction( entityManager -> {
|
||||||
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
||||||
.createStoredProcedureCall( "sp_is_null" );
|
.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.registerParameter( 2, Boolean.class, ParameterMode.OUT );
|
||||||
procedureCall.setParameter( 1, "test" );
|
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
|
@Test
|
||||||
public void testNamedNativeQueryStoredProcedureRefCursorWithJDBC(EntityManagerFactoryScope scope) {
|
public void testNamedNativeQueryStoredProcedureRefCursorWithJDBC(EntityManagerFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
|
|
|
@ -366,7 +366,7 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
|
||||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
||||||
.createStoredProcedureCall( "sp_is_null" );
|
.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.registerParameter( 2, Boolean.class, ParameterMode.OUT );
|
||||||
procedureCall.setParameter( 1, null );
|
procedureCall.setParameter( 1, null );
|
||||||
|
|
||||||
|
@ -378,7 +378,7 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
|
||||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
ProcedureCall procedureCall = entityManager.unwrap( Session.class )
|
||||||
.createStoredProcedureCall( "sp_is_null" );
|
.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.registerParameter( 2, Boolean.class, ParameterMode.OUT );
|
||||||
procedureCall.setParameter( 1, "test" );
|
procedureCall.setParameter( 1, "test" );
|
||||||
|
|
||||||
|
|
|
@ -407,7 +407,7 @@ public class StoredProcedureParameterTypeTest {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
ProcedureCall procedureCall = session.createStoredProcedureCall( "test" );
|
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 );
|
procedureCall.setParameter( 1, null );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,6 +14,7 @@ import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.NamedStoredProcedureQuery;
|
import jakarta.persistence.NamedStoredProcedureQuery;
|
||||||
import jakarta.persistence.ParameterMode;
|
import jakarta.persistence.ParameterMode;
|
||||||
|
import jakarta.persistence.QueryHint;
|
||||||
import jakarta.persistence.StoredProcedureParameter;
|
import jakarta.persistence.StoredProcedureParameter;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@ -23,9 +24,7 @@ import jakarta.persistence.Table;
|
||||||
name = "NumValue.getSomeValues",
|
name = "NumValue.getSomeValues",
|
||||||
procedureName = "f_test_return_cursor",
|
procedureName = "f_test_return_cursor",
|
||||||
resultClasses = NumValue.class,
|
resultClasses = NumValue.class,
|
||||||
parameters = {
|
hints = @QueryHint(name = "org.hibernate.callableFunction", value = "true")
|
||||||
@StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = ResultSet.class)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
public class NumValue implements Serializable {
|
public class NumValue implements Serializable {
|
||||||
@Id
|
@Id
|
||||||
|
|
|
@ -16,15 +16,19 @@ import jakarta.persistence.FieldResult;
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.NamedStoredProcedureQueries;
|
import jakarta.persistence.NamedStoredProcedureQueries;
|
||||||
|
import jakarta.persistence.NamedStoredProcedureQuery;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.OrderColumn;
|
import jakarta.persistence.OrderColumn;
|
||||||
|
import jakarta.persistence.QueryHint;
|
||||||
import jakarta.persistence.SqlResultSetMapping;
|
import jakarta.persistence.SqlResultSetMapping;
|
||||||
import jakarta.persistence.SqlResultSetMappings;
|
import jakarta.persistence.SqlResultSetMappings;
|
||||||
|
import jakarta.persistence.StoredProcedureParameter;
|
||||||
import jakarta.persistence.Temporal;
|
import jakarta.persistence.Temporal;
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
import jakarta.persistence.Version;
|
import jakarta.persistence.Version;
|
||||||
|
|
||||||
import org.hibernate.annotations.NamedNativeQuery;
|
import org.hibernate.annotations.NamedNativeQuery;
|
||||||
|
import org.hibernate.annotations.QueryHints;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Vlad Mihalcea
|
* @author Vlad Mihalcea
|
||||||
|
@ -41,6 +45,15 @@ import org.hibernate.annotations.NamedNativeQuery;
|
||||||
callable = false,
|
callable = false,
|
||||||
resultSetMapping = "person_with_phones_hana"
|
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({
|
@SqlResultSetMappings({
|
||||||
@SqlResultSetMapping(
|
@SqlResultSetMapping(
|
||||||
name = "person_with_phones",
|
name = "person_with_phones",
|
||||||
|
|
|
@ -140,7 +140,26 @@ List<Object[]> postAndComments = entityManager.createNamedQuery("fn_person_and_p
|
||||||
|
|
||||||
is going to throw an `IllegalArgumentException`.
|
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();
|
List<Object[]> postAndComments = entityManager.createStoredProcedureQuery( "fn_person_and_phones", "person_with_phones" ).setParameter( 1, 1L ).getResultList();
|
||||||
```
|
```
|
||||||
|
|
Loading…
Reference in New Issue