HHH-18280 add hibernate.query.pass_procedure_paramater_names setting

This commit is contained in:
Andrea Boriero 2024-07-31 18:50:22 +02:00 committed by Steve Ebersole
parent 8740a832d2
commit 03e48d8355
24 changed files with 254 additions and 123 deletions

View File

@ -1136,12 +1136,6 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
} }
} }
@Override
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) {
// Not sure if it's a JDBC driver issue, but it doesn't work
return false;
}
@Override @Override
public String generatedAs(String generatedAs) { public String generatedAs(String generatedAs) {
return " as (" + generatedAs + ") persisted"; return " as (" + generatedAs + ") persisted";

View File

@ -208,6 +208,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
private boolean orderInsertsEnabled; private boolean orderInsertsEnabled;
private boolean collectionsInDefaultFetchGroupEnabled = true; private boolean collectionsInDefaultFetchGroupEnabled = true;
private boolean UnownedAssociationTransientCheck; private boolean UnownedAssociationTransientCheck;
private boolean passProcedureParameterNames;
// JPA callbacks // JPA callbacks
private final boolean callbacksEnabled; private final boolean callbacksEnabled;
@ -627,6 +628,12 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
configurationSettings, configurationSettings,
isJpaBootstrap() isJpaBootstrap()
); );
this.passProcedureParameterNames = ConfigurationHelper.getBoolean(
AvailableSettings.QUERY_PASS_PROCEDURE_PARAMETER_NAMES,
configurationSettings,
false
);
} }
private boolean disallowBatchUpdates(Dialect dialect, ExtractedDatabaseMetaData meta) { private boolean disallowBatchUpdates(Dialect dialect, ExtractedDatabaseMetaData meta) {
@ -1318,6 +1325,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
return xmlFormatMapper; return xmlFormatMapper;
} }
@Override
public boolean isPassProcedureParameterNames() {
return passProcedureParameterNames;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// In-flight mutation access // In-flight mutation access

View File

@ -512,4 +512,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
public FormatMapper getXmlFormatMapper() { public FormatMapper getXmlFormatMapper() {
return delegate.getXmlFormatMapper(); return delegate.getXmlFormatMapper();
} }
@Override
public boolean isPassProcedureParameterNames() {
return delegate.isPassProcedureParameterNames();
}
} }

View File

@ -374,4 +374,6 @@ public interface SessionFactoryOptions extends QueryEngineOptions {
default JavaType<Object> getDefaultTenantIdentifierJavaType() { default JavaType<Object> getDefaultTenantIdentifierJavaType() {
return ObjectJavaType.INSTANCE; return ObjectJavaType.INSTANCE;
} }
boolean isPassProcedureParameterNames();
} }

View File

@ -242,4 +242,11 @@ public interface QuerySettings {
*/ */
@Deprecated(since="6.0") @Deprecated(since="6.0")
String QUERY_PLAN_CACHE_PARAMETER_METADATA_MAX_SIZE = "hibernate.query.plan_parameter_metadata_max_size"; String QUERY_PLAN_CACHE_PARAMETER_METADATA_MAX_SIZE = "hibernate.query.plan_parameter_metadata_max_size";
/**
* For database supporting name parameters this setting allows to use named parameter is the procedure call.
*
* By default, this is set to false
*/
String QUERY_PASS_PROCEDURE_PARAMETER_NAMES = "hibernate.query.pass_procedure_paramater_names";
} }

View File

@ -857,11 +857,6 @@ public class DB2Dialect extends Dialect {
return false; return false;
} }
@Override
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) throws SQLException {
return false;
}
@Override @Override
public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException { public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
statement.registerOutParameter( col++, Types.REF_CURSOR ); statement.registerOutParameter( col++, Types.REF_CURSOR );

View File

@ -1582,12 +1582,6 @@ public class OracleDialect extends Dialect {
return 1; return 1;
} }
@Override
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) {
// Not sure if it's a JDBC driver issue, but it doesn't work
return false;
}
@Override @Override
public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException { public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException {
return (ResultSet) statement.getObject( name ); return (ResultSet) statement.getObject( name );

View File

@ -1122,11 +1122,6 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
} }
} }
@Override
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) {
return false;
}
@Override @Override
public String generatedAs(String generatedAs) { public String generatedAs(String generatedAs) {
return " as (" + generatedAs + ") persisted"; return " as (" + generatedAs + ") persisted";

View File

@ -8,6 +8,7 @@ package org.hibernate.procedure.internal;
import java.util.List; import java.util.List;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.procedure.spi.FunctionReturnImplementor; 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.ProcedureCallImplementor;
@ -84,7 +85,10 @@ public class DB2CallableStatementSupport extends AbstractStandardCallableStateme
i + offset, i + offset,
procedureCall procedureCall
); );
if ( parameter.getName() != null ) { final SharedSessionContractImplementor session = procedureCall.getSession();
if ( parameter.getName() != null
&& session.getJdbcServices().getExtractedMetaDataSupport().supportsNamedParameters()
&& session.getFactory().getSessionFactoryOptions().isPassProcedureParameterNames() ) {
buffer.append( parameter.getName() ).append( " => ?" ); buffer.append( parameter.getName() ).append( " => ?" );
} }
else { else {

View File

@ -52,7 +52,7 @@ public class FunctionReturnImpl<T> implements FunctionReturnImplementor<T> {
final JdbcCallParameterExtractorImpl<T> parameterExtractor; final JdbcCallParameterExtractorImpl<T> parameterExtractor;
if ( getJdbcTypeCode() == Types.REF_CURSOR ) { if ( getJdbcTypeCode() == Types.REF_CURSOR ) {
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( null, 1 ); refCursorExtractor = new JdbcCallRefCursorExtractorImpl( 1 );
ormType = null; ormType = null;
parameterExtractor = null; parameterExtractor = null;
} }

View File

@ -23,6 +23,7 @@ public class OracleCallableStatementSupport extends StandardCallableStatementSup
super( supportsRefCursors ); super( supportsRefCursors );
} }
@Override
protected void appendNameParameter( protected void appendNameParameter(
StringBuilder buffer, StringBuilder buffer,
ProcedureParameterImplementor parameter, ProcedureParameterImplementor parameter,

View File

@ -6,15 +6,11 @@
*/ */
package org.hibernate.procedure.internal; package org.hibernate.procedure.internal;
import java.sql.CallableStatement;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData; import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.procedure.ParameterTypeException; import org.hibernate.procedure.ParameterTypeException;
import org.hibernate.procedure.spi.NamedCallableQueryMemento; import org.hibernate.procedure.spi.NamedCallableQueryMemento;
import org.hibernate.procedure.spi.ParameterStrategy; import org.hibernate.procedure.spi.ParameterStrategy;
@ -29,7 +25,6 @@ import org.hibernate.sql.exec.internal.JdbcCallParameterExtractorImpl;
import org.hibernate.sql.exec.internal.JdbcCallParameterRegistrationImpl; import org.hibernate.sql.exec.internal.JdbcCallParameterRegistrationImpl;
import org.hibernate.sql.exec.internal.JdbcCallRefCursorExtractorImpl; import org.hibernate.sql.exec.internal.JdbcCallRefCursorExtractorImpl;
import org.hibernate.sql.exec.internal.JdbcParameterImpl; 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.JdbcCallParameterRegistration;
import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBinder;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
@ -138,45 +133,49 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
bindableType = null; bindableType = null;
} }
final SharedSessionContractImplementor session = procedureCall.getSession();
final OutputableType<T> typeToUse = (OutputableType<T>) BindingTypeHelper.INSTANCE.resolveTemporalPrecision( final OutputableType<T> typeToUse = (OutputableType<T>) BindingTypeHelper.INSTANCE.resolveTemporalPrecision(
binding == null ? null : binding.getExplicitTemporalPrecision(), binding == null ? null : binding.getExplicitTemporalPrecision(),
bindableType, bindableType,
procedureCall.getSession().getFactory() session.getFactory()
); );
final String jdbcParamName; final String jdbcParamName;
final JdbcParameterBinder parameterBinder; final JdbcParameterBinder parameterBinder;
final JdbcCallRefCursorExtractorImpl refCursorExtractor; final JdbcCallRefCursorExtractorImpl refCursorExtractor;
final JdbcCallParameterExtractorImpl<T> parameterExtractor; final JdbcCallParameterExtractorImpl<T> parameterExtractor;
final ExtractedDatabaseMetaData databaseMetaData = procedureCall.getSession() final ExtractedDatabaseMetaData databaseMetaData = session
.getFactory() .getFactory()
.getJdbcServices() .getJdbcServices()
.getJdbcEnvironment() .getJdbcEnvironment()
.getExtractedDatabaseMetaData(); .getExtractedDatabaseMetaData();
final boolean passProcedureParameterNames = session.getFactory()
.getSessionFactoryOptions()
.isPassProcedureParameterNames();
switch ( mode ) { switch ( mode ) {
case REF_CURSOR: case REF_CURSOR:
jdbcParamName = this.name != null && databaseMetaData.supportsNamedParameters() ? this.name : null; jdbcParamName = this.name != null && databaseMetaData.supportsNamedParameters() && passProcedureParameterNames ? this.name : null;
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( jdbcParamName, startIndex ); refCursorExtractor = new JdbcCallRefCursorExtractorImpl( startIndex );
parameterBinder = null; parameterBinder = null;
parameterExtractor = null; parameterExtractor = null;
break; break;
case IN: case IN:
jdbcParamName = getJdbcParamName( procedureCall, isNamed, typeToUse, databaseMetaData ); jdbcParamName = getJdbcParamName( procedureCall, isNamed, passProcedureParameterNames, typeToUse, databaseMetaData );
validateBindableType( typeToUse, startIndex ); validateBindableType( typeToUse, startIndex );
parameterBinder = getParameterBinder( typeToUse, jdbcParamName ); parameterBinder = getParameterBinder( typeToUse, jdbcParamName );
parameterExtractor = null; parameterExtractor = null;
refCursorExtractor = null; refCursorExtractor = null;
break; break;
case INOUT: case INOUT:
jdbcParamName = getJdbcParamName( procedureCall, isNamed, typeToUse, databaseMetaData ); jdbcParamName = getJdbcParamName( procedureCall, isNamed, passProcedureParameterNames, typeToUse, databaseMetaData );
validateBindableType( typeToUse, startIndex ); validateBindableType( typeToUse, startIndex );
parameterBinder = getParameterBinder( typeToUse, jdbcParamName ); parameterBinder = getParameterBinder( typeToUse, jdbcParamName );
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), jdbcParamName, startIndex, typeToUse ); parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), jdbcParamName, startIndex, typeToUse );
refCursorExtractor = null; refCursorExtractor = null;
break; break;
default: default:
jdbcParamName = getJdbcParamName( procedureCall, isNamed, typeToUse, databaseMetaData ); jdbcParamName = getJdbcParamName( procedureCall, isNamed, passProcedureParameterNames, typeToUse, databaseMetaData );
validateBindableType( typeToUse, startIndex ); validateBindableType( typeToUse, startIndex );
parameterBinder = null; parameterBinder = null;
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), jdbcParamName, startIndex, typeToUse ); parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), jdbcParamName, startIndex, typeToUse );
@ -190,9 +189,10 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
private String getJdbcParamName( private String getJdbcParamName(
ProcedureCallImplementor<?> procedureCall, ProcedureCallImplementor<?> procedureCall,
boolean isNamed, boolean isNamed,
boolean passProcedureParameterNames,
OutputableType<T> typeToUse, OutputableType<T> typeToUse,
ExtractedDatabaseMetaData databaseMetaData) { ExtractedDatabaseMetaData databaseMetaData) {
return isNamed && canDoNameParameterBinding( typeToUse, procedureCall, databaseMetaData ) ? this.name : null; return isNamed && passProcedureParameterNames && canDoNameParameterBinding( typeToUse, procedureCall, databaseMetaData ) ? this.name : null;
} }
private void validateBindableType(BindableType<T> bindableType, int startIndex) { private void validateBindableType(BindableType<T> bindableType, int startIndex) {
@ -221,32 +221,8 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
} }
if ( typeToUse instanceof BasicType<?> ) { if ( typeToUse instanceof BasicType<?> ) {
if ( name == null ) {
return new JdbcParameterImpl( (BasicType<T>) typeToUse ); return new JdbcParameterImpl( (BasicType<T>) typeToUse );
} }
else {
return new JdbcParameterImpl( (BasicType<T>) typeToUse ) {
@Override
protected void bindParameterValue(
JdbcMapping jdbcMapping,
PreparedStatement statement,
Object bindValue,
int startPosition,
ExecutionContext executionContext) throws SQLException {
jdbcMapping.getJdbcValueBinder().bind(
(CallableStatement) statement,
bindValue,
name,
executionContext.getSession()
);
}
@Override
public String toString() {
return "JdbcParameter(" + name + ")";
}
};
}
}
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -18,6 +18,7 @@ public class SQLServerCallableStatementSupport extends StandardCallableStatement
super( false ); super( false );
} }
@Override
protected void appendNameParameter(StringBuilder buffer, ProcedureParameterImplementor parameter, JdbcCallParameterRegistration registration) { protected void appendNameParameter(StringBuilder buffer, ProcedureParameterImplementor parameter, JdbcCallParameterRegistration registration) {
buffer.append( '@' ).append( parameter.getName() ).append( " = ?" ); buffer.append( '@' ).append( parameter.getName() ).append( " = ?" );
} }

View File

@ -91,7 +91,9 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
i + offset, i + offset,
procedureCall procedureCall
); );
if ( parameter.getName() != null ) { if ( parameter.getName() != null
&& session.getJdbcServices().getExtractedMetaDataSupport().supportsNamedParameters()
&& session.getFactory().getSessionFactoryOptions().isPassProcedureParameterNames() ) {
appendNameParameter( buffer, parameter, registration ); appendNameParameter( buffer, parameter, registration );
} }
else { else {

View File

@ -9,6 +9,7 @@ package org.hibernate.procedure.internal;
import java.util.List; import java.util.List;
import org.hibernate.QueryException; import org.hibernate.QueryException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.procedure.spi.FunctionReturnImplementor; import org.hibernate.procedure.spi.FunctionReturnImplementor;
import org.hibernate.procedure.spi.ProcedureCallImplementor; import org.hibernate.procedure.spi.ProcedureCallImplementor;
import org.hibernate.procedure.spi.ProcedureParameterImplementor; import org.hibernate.procedure.spi.ProcedureParameterImplementor;
@ -80,7 +81,10 @@ public class SybaseCallableStatementSupport extends AbstractStandardCallableStat
i + offset, i + offset,
procedureCall procedureCall
); );
if ( parameter.getName() != null ) { final SharedSessionContractImplementor session = procedureCall.getSession();
if ( parameter.getName() != null
&& session.getJdbcServices().getExtractedMetaDataSupport().supportsNamedParameters()
&& session.getFactory().getSessionFactoryOptions().isPassProcedureParameterNames() ) {
buffer.append("@").append( parameter.getName() ).append( " = ?" ); buffer.append("@").append( parameter.getName() ).append( " = ?" );
} }
else { else {

View File

@ -50,21 +50,9 @@ public class JdbcCallParameterExtractorImpl<T> implements JdbcCallParameterExtra
CallableStatement callableStatement, CallableStatement callableStatement,
boolean shouldUseJdbcNamedParameters, boolean shouldUseJdbcNamedParameters,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
final boolean useNamed = shouldUseJdbcNamedParameters
&& parameterName != null;
// todo (6.0) : we should just ask BasicValuedExpressibleType for the JdbcValueExtractor...
try { try {
if ( useNamed ) {
return ormType.extract( callableStatement, parameterName, session );
}
else {
return ormType.extract( callableStatement, parameterPosition, session ); return ormType.extract( callableStatement, parameterPosition, session );
} }
}
catch (SQLException e) { catch (SQLException e) {
throw session.getJdbcServices().getSqlExceptionHelper().convert( throw session.getJdbcServices().getSqlExceptionHelper().convert(
e, e,

View File

@ -101,16 +101,9 @@ public class JdbcCallParameterRegistrationImpl implements JdbcCallParameterRegis
private void registerRefCursorParameter( private void registerRefCursorParameter(
CallableStatement callableStatement, CallableStatement callableStatement,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
if ( name != null ) {
session.getFactory().getServiceRegistry()
.requireService( RefCursorSupport.class )
.registerRefCursorParameter( callableStatement, name );
}
else {
session.getFactory().getServiceRegistry() session.getFactory().getServiceRegistry()
.requireService( RefCursorSupport.class ) .requireService( RefCursorSupport.class )
.registerRefCursorParameter( callableStatement, jdbcParameterPositionStart ); .registerRefCursorParameter( callableStatement, jdbcParameterPositionStart );
}
} }
@ -119,13 +112,8 @@ public class JdbcCallParameterRegistrationImpl implements JdbcCallParameterRegis
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
final JdbcType sqlTypeDescriptor = ormType.getJdbcType(); final JdbcType sqlTypeDescriptor = ormType.getJdbcType();
try { try {
if ( name != null ) {
sqlTypeDescriptor.registerOutParameter( callableStatement, name );
}
else {
sqlTypeDescriptor.registerOutParameter( callableStatement, jdbcParameterPositionStart ); sqlTypeDescriptor.registerOutParameter( callableStatement, jdbcParameterPositionStart );
} }
}
catch (SQLException e) { catch (SQLException e) {
throw session.getJdbcServices().getSqlExceptionHelper().convert( throw session.getJdbcServices().getSqlExceptionHelper().convert(
e, e,

View File

@ -21,13 +21,10 @@ import org.hibernate.sql.exec.spi.JdbcCallRefCursorExtractor;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class JdbcCallRefCursorExtractorImpl implements JdbcCallRefCursorExtractor { public class JdbcCallRefCursorExtractorImpl implements JdbcCallRefCursorExtractor {
private final String jdbcParameterName;
private final int jdbcParameterPosition; private final int jdbcParameterPosition;
public JdbcCallRefCursorExtractorImpl( public JdbcCallRefCursorExtractorImpl(
String jdbcParameterName,
int jdbcParameterPosition) { int jdbcParameterPosition) {
this.jdbcParameterName = jdbcParameterName;
this.jdbcParameterPosition = jdbcParameterPosition; this.jdbcParameterPosition = jdbcParameterPosition;
} }
@ -39,19 +36,9 @@ public class JdbcCallRefCursorExtractorImpl implements JdbcCallRefCursorExtracto
.getJdbcEnvironment() .getJdbcEnvironment()
.getExtractedDatabaseMetaData() .getExtractedDatabaseMetaData()
.supportsNamedParameters(); .supportsNamedParameters();
final boolean useNamed = supportsNamedParameters && jdbcParameterName != null;
if ( useNamed ) {
return session.getFactory()
.getServiceRegistry()
.requireService( RefCursorSupport.class )
.getResultSet( callableStatement, jdbcParameterName );
}
else {
return session.getFactory() return session.getFactory()
.getServiceRegistry() .getServiceRegistry()
.requireService( RefCursorSupport.class ) .requireService( RefCursorSupport.class )
.getResultSet( callableStatement, jdbcParameterPosition ); .getResultSet( callableStatement, jdbcParameterPosition );
} }
} }
}

View File

@ -16,6 +16,7 @@ import java.time.ZoneOffset;
import java.util.List; import java.util.List;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DB2Dialect;
import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCall;
import org.hibernate.query.procedure.ProcedureParameter; import org.hibernate.query.procedure.ProcedureParameter;
@ -29,6 +30,7 @@ import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
@ -58,13 +60,16 @@ import static org.junit.jupiter.api.Assertions.fail;
/** /**
* @author Marco Belladelli * @author Marco Belladelli
*/ */
@Jpa( annotatedClasses = { @Jpa(
annotatedClasses = {
Person.class, Person.class,
Phone.class, Phone.class,
Vote.class, Vote.class,
DB2StoredProcedureTest.IdHolder.class, DB2StoredProcedureTest.IdHolder.class,
DB2StoredProcedureTest.Address.class, DB2StoredProcedureTest.Address.class,
} ) },
properties = @Setting(name = AvailableSettings.QUERY_PASS_PROCEDURE_PARAMETER_NAMES, value = "true")
)
@RequiresDialect( value = DB2Dialect.class ) @RequiresDialect( value = DB2Dialect.class )
@Jira( "https://hibernate.atlassian.net/browse/HHH-18332" ) @Jira( "https://hibernate.atlassian.net/browse/HHH-18332" )
public class DB2StoredProcedureTest { public class DB2StoredProcedureTest {

View File

@ -19,6 +19,7 @@ import java.util.List;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.annotations.QueryHints; import org.hibernate.annotations.QueryHints;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.OracleDialect;
import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCall;
import org.hibernate.query.procedure.ProcedureParameter; import org.hibernate.query.procedure.ProcedureParameter;
@ -31,6 +32,7 @@ import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -65,7 +67,8 @@ import static org.junit.jupiter.api.Assertions.fail;
OracleStoredProcedureTest.IdHolder.class, OracleStoredProcedureTest.IdHolder.class,
Vote.class, Vote.class,
OracleStoredProcedureTest.Address.class OracleStoredProcedureTest.Address.class
} },
properties = @Setting( name = AvailableSettings.QUERY_PASS_PROCEDURE_PARAMETER_NAMES, value = "true")
) )
@RequiresDialect(value = OracleDialect.class) @RequiresDialect(value = OracleDialect.class)
public class OracleStoredProcedureTest { public class OracleStoredProcedureTest {

View File

@ -18,6 +18,7 @@ import java.time.ZoneOffset;
import java.util.List; import java.util.List;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCall;
import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.StandardBasicTypes;
@ -26,6 +27,7 @@ import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -53,7 +55,8 @@ import static org.junit.Assert.fail;
Person.class, Person.class,
Phone.class, Phone.class,
PostgreSQLStoredProcedureTest.Address.class PostgreSQLStoredProcedureTest.Address.class
} },
properties = @Setting( name = AvailableSettings.QUERY_PASS_PROCEDURE_PARAMETER_NAMES, value = "true")
) )
@RequiresDialect(value = PostgreSQLDialect.class) @RequiresDialect(value = PostgreSQLDialect.class)
public class PostgreSQLStoredProcedureTest { public class PostgreSQLStoredProcedureTest {

View File

@ -0,0 +1,159 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.procedure;
import java.sql.CallableStatement;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.regex.Pattern;
import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ParameterMode;
import jakarta.persistence.StoredProcedureQuery;
import jakarta.persistence.Table;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.testing.transaction.TransactionUtil.doInAutoCommit;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author Vlad Mihalcea
*/
@RequiresDialect(value = SQLServerDialect.class, majorVersion = 11)
@Jpa(
annotatedClasses = {
Person.class,
Phone.class,
SQLServerStoredProcedureForcePositionalTest.Address.class
},
properties = @Setting( name = AvailableSettings.QUERY_PASS_PROCEDURE_PARAMETER_NAMES, value = "false")
)
public class SQLServerStoredProcedureForcePositionalTest {
private static final String CITY = "London";
private static final String STREET = "Lollard Street";
private static final String ZIP = "SE116UG";
@BeforeEach
public void init(EntityManagerFactoryScope scope) {
doInAutoCommit(
"DROP PROCEDURE sp_count_phones",
"CREATE PROCEDURE sp_count_phones " +
" @personId INT, " +
" @phoneCount INT OUTPUT " +
"AS " +
"BEGIN " +
" SELECT @phoneCount = COUNT(*) " +
" FROM Phone " +
" WHERE person_id = @personId " +
"END"
);
scope.inTransaction( entityManager -> {
Person person1 = new Person( 1L, "John Doe" );
person1.setNickName( "JD" );
person1.setAddress( "Earth" );
person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 )
.toInstant( ZoneOffset.UTC ) ) );
entityManager.persist( person1 );
Phone phone1 = new Phone( "123-456-7890" );
phone1.setId( 1L );
person1.addPhone( phone1 );
Phone phone2 = new Phone( "098_765-4321" );
phone2.setId( 2L );
person1.addPhone( phone2 );
Address address = new Address( 1l, STREET, CITY, ZIP );
entityManager.persist( address );
} );
}
@AfterEach
public void tearDown(EntityManagerFactoryScope scope) {
scope.releaseEntityManagerFactory();
}
@Test
public void testStoredProcedure(EntityManagerFactoryScope scope) {
scope.inTransaction( entityManager -> {
StoredProcedureQuery query = entityManager.createStoredProcedureQuery( "sp_count_phones" );
query.registerStoredProcedureParameter( "personId2", Long.class, ParameterMode.IN );
query.registerStoredProcedureParameter( "phoneCount", Long.class, ParameterMode.OUT );
query.setParameter( "personId2", 1L );
query.execute();
Long phoneCount = (Long) query.getOutputParameterValue( "phoneCount" );
assertEquals( Long.valueOf( 2 ), phoneCount );
} );
}
@Entity(name = "Address")
@Table(name = "ADDRESS_TABLE")
public static class Address {
@Id
@Column(name = "ID")
private long id;
@Column(name = "STREET")
private String street;
@Column(name = "CITY")
private String city;
@Column(name = "ZIP")
private String zip;
public Address() {
}
public Address(long id, String street, String city, String zip) {
this.id = id;
this.street = street;
this.city = city;
this.zip = zip;
}
public long getId() {
return id;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getZip() {
return zip;
}
}
}

View File

@ -15,12 +15,14 @@ import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -48,7 +50,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
Person.class, Person.class,
Phone.class, Phone.class,
SQLServerStoredProcedureTest.Address.class SQLServerStoredProcedureTest.Address.class
} },
properties = @Setting( name = AvailableSettings.QUERY_PASS_PROCEDURE_PARAMETER_NAMES, value = "true")
) )
public class SQLServerStoredProcedureTest { public class SQLServerStoredProcedureTest {

View File

@ -10,6 +10,7 @@ import java.sql.Timestamp;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.dialect.SybaseASEDialect;
import org.hibernate.jpa.HibernateHints; import org.hibernate.jpa.HibernateHints;
@ -18,6 +19,7 @@ import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.Assert; import org.junit.Assert;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -39,7 +41,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
annotatedClasses = { annotatedClasses = {
Person.class, Person.class,
Phone.class, Phone.class,
} },
properties = @Setting( name = AvailableSettings.QUERY_PASS_PROCEDURE_PARAMETER_NAMES, value = "true")
) )
public class SybaseStoredProcedureTest { public class SybaseStoredProcedureTest {