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
public String generatedAs(String generatedAs) {
return " as (" + generatedAs + ") persisted";

View File

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

View File

@ -512,4 +512,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
public FormatMapper 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() {
return ObjectJavaType.INSTANCE;
}
boolean isPassProcedureParameterNames();
}

View File

@ -242,4 +242,11 @@ public interface QuerySettings {
*/
@Deprecated(since="6.0")
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;
}
@Override
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) throws SQLException {
return false;
}
@Override
public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
statement.registerOutParameter( col++, Types.REF_CURSOR );

View File

@ -1582,12 +1582,6 @@ public class OracleDialect extends Dialect {
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
public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException {
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
public String generatedAs(String generatedAs) {
return " as (" + generatedAs + ") persisted";

View File

@ -8,6 +8,7 @@ package org.hibernate.procedure.internal;
import java.util.List;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.procedure.spi.FunctionReturnImplementor;
import org.hibernate.procedure.spi.ParameterStrategy;
import org.hibernate.procedure.spi.ProcedureCallImplementor;
@ -84,7 +85,10 @@ public class DB2CallableStatementSupport extends AbstractStandardCallableStateme
i + offset,
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( " => ?" );
}
else {

View File

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

View File

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

View File

@ -6,15 +6,11 @@
*/
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.Objects;
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.spi.NamedCallableQueryMemento;
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.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.type.BasicType;
@ -138,45 +133,49 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
bindableType = null;
}
final SharedSessionContractImplementor session = procedureCall.getSession();
final OutputableType<T> typeToUse = (OutputableType<T>) BindingTypeHelper.INSTANCE.resolveTemporalPrecision(
binding == null ? null : binding.getExplicitTemporalPrecision(),
bindableType,
procedureCall.getSession().getFactory()
session.getFactory()
);
final String jdbcParamName;
final JdbcParameterBinder parameterBinder;
final JdbcCallRefCursorExtractorImpl refCursorExtractor;
final JdbcCallParameterExtractorImpl<T> parameterExtractor;
final ExtractedDatabaseMetaData databaseMetaData = procedureCall.getSession()
final ExtractedDatabaseMetaData databaseMetaData = session
.getFactory()
.getJdbcServices()
.getJdbcEnvironment()
.getExtractedDatabaseMetaData();
final boolean passProcedureParameterNames = session.getFactory()
.getSessionFactoryOptions()
.isPassProcedureParameterNames();
switch ( mode ) {
case REF_CURSOR:
jdbcParamName = this.name != null && databaseMetaData.supportsNamedParameters() ? this.name : null;
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( jdbcParamName, startIndex );
jdbcParamName = this.name != null && databaseMetaData.supportsNamedParameters() && passProcedureParameterNames ? this.name : null;
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( startIndex );
parameterBinder = null;
parameterExtractor = null;
break;
case IN:
jdbcParamName = getJdbcParamName( procedureCall, isNamed, typeToUse, databaseMetaData );
jdbcParamName = getJdbcParamName( procedureCall, isNamed, passProcedureParameterNames, typeToUse, databaseMetaData );
validateBindableType( typeToUse, startIndex );
parameterBinder = getParameterBinder( typeToUse, jdbcParamName );
parameterExtractor = null;
refCursorExtractor = null;
break;
case INOUT:
jdbcParamName = getJdbcParamName( procedureCall, isNamed, typeToUse, databaseMetaData );
jdbcParamName = getJdbcParamName( procedureCall, isNamed, passProcedureParameterNames, typeToUse, databaseMetaData );
validateBindableType( typeToUse, startIndex );
parameterBinder = getParameterBinder( typeToUse, jdbcParamName );
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), jdbcParamName, startIndex, typeToUse );
refCursorExtractor = null;
break;
default:
jdbcParamName = getJdbcParamName( procedureCall, isNamed, typeToUse, databaseMetaData );
jdbcParamName = getJdbcParamName( procedureCall, isNamed, passProcedureParameterNames, typeToUse, databaseMetaData );
validateBindableType( typeToUse, startIndex );
parameterBinder = null;
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), jdbcParamName, startIndex, typeToUse );
@ -190,9 +189,10 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
private String getJdbcParamName(
ProcedureCallImplementor<?> procedureCall,
boolean isNamed,
boolean passProcedureParameterNames,
OutputableType<T> typeToUse,
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) {
@ -221,31 +221,7 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
}
if ( typeToUse instanceof BasicType<?> ) {
if ( name == null ) {
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 + ")";
}
};
}
return new JdbcParameterImpl( (BasicType<T>) typeToUse );
}
throw new UnsupportedOperationException();

View File

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

View File

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

View File

@ -9,6 +9,7 @@ package org.hibernate.procedure.internal;
import java.util.List;
import org.hibernate.QueryException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.procedure.spi.FunctionReturnImplementor;
import org.hibernate.procedure.spi.ProcedureCallImplementor;
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
@ -80,7 +81,10 @@ public class SybaseCallableStatementSupport extends AbstractStandardCallableStat
i + offset,
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( " = ?" );
}
else {

View File

@ -50,20 +50,8 @@ public class JdbcCallParameterExtractorImpl<T> implements JdbcCallParameterExtra
CallableStatement callableStatement,
boolean shouldUseJdbcNamedParameters,
SharedSessionContractImplementor session) {
final boolean useNamed = shouldUseJdbcNamedParameters
&& parameterName != null;
// todo (6.0) : we should just ask BasicValuedExpressibleType for the JdbcValueExtractor...
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) {
throw session.getJdbcServices().getSqlExceptionHelper().convert(

View File

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

View File

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

View File

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

View File

@ -19,6 +19,7 @@ import java.util.List;
import org.hibernate.Session;
import org.hibernate.annotations.QueryHints;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.procedure.ProcedureCall;
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.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;
@ -65,7 +67,8 @@ import static org.junit.jupiter.api.Assertions.fail;
OracleStoredProcedureTest.IdHolder.class,
Vote.class,
OracleStoredProcedureTest.Address.class
}
},
properties = @Setting( name = AvailableSettings.QUERY_PASS_PROCEDURE_PARAMETER_NAMES, value = "true")
)
@RequiresDialect(value = OracleDialect.class)
public class OracleStoredProcedureTest {

View File

@ -18,6 +18,7 @@ import java.time.ZoneOffset;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.procedure.ProcedureCall;
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.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.Assertions;
import org.junit.jupiter.api.BeforeEach;
@ -53,7 +55,8 @@ import static org.junit.Assert.fail;
Person.class,
Phone.class,
PostgreSQLStoredProcedureTest.Address.class
}
},
properties = @Setting( name = AvailableSettings.QUERY_PASS_PROCEDURE_PARAMETER_NAMES, value = "true")
)
@RequiresDialect(value = PostgreSQLDialect.class)
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 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;
@ -48,7 +50,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
Person.class,
Phone.class,
SQLServerStoredProcedureTest.Address.class
}
},
properties = @Setting( name = AvailableSettings.QUERY_PASS_PROCEDURE_PARAMETER_NAMES, value = "true")
)
public class SQLServerStoredProcedureTest {

View File

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