HHH-18280 Support named procedure parameters down to the JDBC level
This commit is contained in:
parent
79480ab490
commit
8766a8e012
|
@ -1501,12 +1501,6 @@ public class OracleLegacyDialect 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 );
|
||||
|
|
|
@ -54,7 +54,7 @@ import org.hibernate.mapping.UserDefinedType;
|
|||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.procedure.internal.StandardCallableStatementSupport;
|
||||
import org.hibernate.procedure.internal.OracleCallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
|
@ -1314,7 +1314,7 @@ public class OracleDialect extends Dialect {
|
|||
@Override
|
||||
public CallableStatementSupport getCallableStatementSupport() {
|
||||
// Oracle supports returning cursors
|
||||
return StandardCallableStatementSupport.REF_CURSOR_INSTANCE;
|
||||
return OracleCallableStatementSupport.REF_CURSOR_INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -54,6 +54,8 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
|||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.procedure.internal.SQLServerCallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
import org.hibernate.query.sqm.CastType;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.query.sqm.IntervalType;
|
||||
|
@ -1122,7 +1124,6 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
@Override
|
||||
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) {
|
||||
// Not sure if it's a JDBC driver issue, but it doesn't work
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1168,4 +1169,10 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
public boolean supportsFromClauseInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatementSupport getCallableStatementSupport() {
|
||||
return SQLServerCallableStatementSupport.INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -84,8 +84,8 @@ public class DB2CallableStatementSupport extends AbstractStandardCallableStateme
|
|||
i + offset,
|
||||
procedureCall
|
||||
);
|
||||
if ( registration.getName() != null ) {
|
||||
buffer.append( ':' ).append( registration.getName() );
|
||||
if ( parameter.getName() != null ) {
|
||||
buffer.append( ':' ).append( parameter.getName() );
|
||||
}
|
||||
else {
|
||||
buffer.append( "?" );
|
||||
|
|
|
@ -69,8 +69,8 @@ public class JTDSCallableStatementSupport extends AbstractStandardCallableStatem
|
|||
i + offset,
|
||||
procedureCall
|
||||
);
|
||||
if ( registration.getName() != null ) {
|
||||
buffer.append( '@' ).append( registration.getName() ).append( "=?" );
|
||||
if ( parameter.getName() != null ) {
|
||||
buffer.append( '@' ).append( parameter.getName() ).append( "=?" );
|
||||
}
|
||||
else {
|
||||
buffer.append( "?" );
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.procedure.internal;
|
||||
|
||||
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
|
||||
/**
|
||||
* Standard implementation of {@link org.hibernate.procedure.spi.CallableStatementSupport}.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class OracleCallableStatementSupport extends StandardCallableStatementSupport {
|
||||
|
||||
public static final StandardCallableStatementSupport REF_CURSOR_INSTANCE = new OracleCallableStatementSupport( true );
|
||||
|
||||
|
||||
public OracleCallableStatementSupport(boolean supportsRefCursors) {
|
||||
super( supportsRefCursors );
|
||||
}
|
||||
|
||||
protected void appendNameParameter(
|
||||
StringBuilder buffer,
|
||||
ProcedureParameterImplementor parameter,
|
||||
JdbcCallParameterRegistration registration) {
|
||||
buffer.append( parameter.getName() ).append( " => ?" );
|
||||
}
|
||||
|
||||
}
|
|
@ -156,6 +156,9 @@ public class PostgreSQLCallableStatementSupport extends AbstractStandardCallable
|
|||
);
|
||||
final OutputableType<?> type = registration.getParameterType();
|
||||
final String castType;
|
||||
if ( parameter.getName() != null ) {
|
||||
buffer.append( parameter.getName() ).append( " => " );
|
||||
}
|
||||
if ( type != null && type.getJdbcType() instanceof AbstractPostgreSQLStructJdbcType ) {
|
||||
// We have to cast struct type parameters so that PostgreSQL understands nulls
|
||||
castType = ( (AbstractPostgreSQLStructJdbcType) type.getJdbcType() ).getStructTypeName();
|
||||
|
@ -164,12 +167,7 @@ public class PostgreSQLCallableStatementSupport extends AbstractStandardCallable
|
|||
else {
|
||||
castType = null;
|
||||
}
|
||||
if ( registration.getName() != null ) {
|
||||
buffer.append( ':' ).append( registration.getName() );
|
||||
}
|
||||
else {
|
||||
buffer.append( "?" );
|
||||
}
|
||||
buffer.append( "?" );
|
||||
if ( castType != null ) {
|
||||
buffer.append( " as " ).append( castType ).append( ')' );
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
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;
|
||||
|
@ -144,36 +145,38 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
|||
);
|
||||
|
||||
final String jdbcParamName;
|
||||
if ( isNamed && canDoNameParameterBinding( typeToUse, procedureCall ) ) {
|
||||
jdbcParamName = this.name;
|
||||
}
|
||||
else {
|
||||
jdbcParamName = null;
|
||||
}
|
||||
|
||||
final JdbcParameterBinder parameterBinder;
|
||||
final JdbcCallRefCursorExtractorImpl refCursorExtractor;
|
||||
final JdbcCallParameterExtractorImpl<T> parameterExtractor;
|
||||
final ExtractedDatabaseMetaData databaseMetaData = procedureCall.getSession()
|
||||
.getFactory()
|
||||
.getJdbcServices()
|
||||
.getJdbcEnvironment()
|
||||
.getExtractedDatabaseMetaData();
|
||||
|
||||
switch ( mode ) {
|
||||
case REF_CURSOR:
|
||||
jdbcParamName = this.name != null && databaseMetaData.supportsNamedParameters() ? this.name : null;
|
||||
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( jdbcParamName, startIndex );
|
||||
parameterBinder = null;
|
||||
parameterExtractor = null;
|
||||
break;
|
||||
case IN:
|
||||
jdbcParamName = getJdbcParamName( procedureCall, isNamed, typeToUse, databaseMetaData );
|
||||
validateBindableType( typeToUse, startIndex );
|
||||
parameterBinder = getParameterBinder( typeToUse, jdbcParamName );
|
||||
parameterExtractor = null;
|
||||
refCursorExtractor = null;
|
||||
break;
|
||||
case INOUT:
|
||||
jdbcParamName = getJdbcParamName( procedureCall, isNamed, 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 );
|
||||
validateBindableType( typeToUse, startIndex );
|
||||
parameterBinder = null;
|
||||
parameterExtractor = new JdbcCallParameterExtractorImpl<>( procedureCall.getProcedureName(), jdbcParamName, startIndex, typeToUse );
|
||||
|
@ -184,6 +187,14 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
|||
return new JdbcCallParameterRegistrationImpl( jdbcParamName, startIndex, mode, typeToUse, parameterBinder, parameterExtractor, refCursorExtractor );
|
||||
}
|
||||
|
||||
private String getJdbcParamName(
|
||||
ProcedureCallImplementor<?> procedureCall,
|
||||
boolean isNamed,
|
||||
OutputableType<T> typeToUse,
|
||||
ExtractedDatabaseMetaData databaseMetaData) {
|
||||
return isNamed && canDoNameParameterBinding( typeToUse, procedureCall, databaseMetaData ) ? this.name : null;
|
||||
}
|
||||
|
||||
private void validateBindableType(BindableType<T> bindableType, int startIndex) {
|
||||
if ( bindableType == null ) {
|
||||
throw new ParameterTypeException(
|
||||
|
@ -210,7 +221,7 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
|||
}
|
||||
|
||||
if ( typeToUse instanceof BasicType<?> ) {
|
||||
if ( name == null ) {
|
||||
if ( name == null ) {
|
||||
return new JdbcParameterImpl( (BasicType<T>) typeToUse );
|
||||
}
|
||||
else {
|
||||
|
@ -242,12 +253,8 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
|||
|
||||
private boolean canDoNameParameterBinding(
|
||||
BindableType<?> hibernateType,
|
||||
ProcedureCallImplementor<?> procedureCall) {
|
||||
final ExtractedDatabaseMetaData databaseMetaData = procedureCall.getSession()
|
||||
.getFactory()
|
||||
.getJdbcServices()
|
||||
.getJdbcEnvironment()
|
||||
.getExtractedDatabaseMetaData();
|
||||
ProcedureCallImplementor<?> procedureCall,
|
||||
ExtractedDatabaseMetaData databaseMetaData) {
|
||||
return procedureCall.getFunctionReturn() == null
|
||||
&& databaseMetaData.supportsNamedParameters()
|
||||
&& hibernateType instanceof ProcedureParameterNamedBinder
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.procedure.internal;
|
||||
|
||||
import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
|
||||
public class SQLServerCallableStatementSupport extends StandardCallableStatementSupport {
|
||||
|
||||
public static final StandardCallableStatementSupport INSTANCE = new SQLServerCallableStatementSupport( );
|
||||
|
||||
|
||||
private SQLServerCallableStatementSupport() {
|
||||
super( false );
|
||||
}
|
||||
|
||||
protected void appendNameParameter(StringBuilder buffer, ProcedureParameterImplementor parameter, JdbcCallParameterRegistration registration) {
|
||||
buffer.append( '@' ).append( parameter.getName() ).append( " = ?" );
|
||||
}
|
||||
}
|
|
@ -91,8 +91,8 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
|
|||
i + offset,
|
||||
procedureCall
|
||||
);
|
||||
if ( registration.getName() != null ) {
|
||||
buffer.append( ':' ).append( registration.getName() );
|
||||
if ( parameter.getName() != null ) {
|
||||
appendNameParameter( buffer, parameter, registration );
|
||||
}
|
||||
else {
|
||||
buffer.append( "?" );
|
||||
|
@ -108,6 +108,13 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
|
|||
return builder.buildJdbcCall();
|
||||
}
|
||||
|
||||
protected void appendNameParameter(
|
||||
StringBuilder buffer,
|
||||
ProcedureParameterImplementor parameter,
|
||||
JdbcCallParameterRegistration registration) {
|
||||
buffer.append( '?' );
|
||||
}
|
||||
|
||||
private void verifyRefCursorSupport(Dialect dialect) {
|
||||
if ( ! supportsRefCursors ) {
|
||||
throw new QueryException( "Dialect [" + dialect.getClass().getName() + "] not known to support REF_CURSOR parameters" );
|
||||
|
|
|
@ -80,10 +80,12 @@ public class SybaseCallableStatementSupport extends AbstractStandardCallableStat
|
|||
i + offset,
|
||||
procedureCall
|
||||
);
|
||||
if ( registration.getName() != null ) {
|
||||
throw new QueryException( "The JDBC driver does not support named parameters" );
|
||||
if ( parameter.getName() != null ) {
|
||||
buffer.append("@").append( parameter.getName() ).append( " = ?" );
|
||||
}
|
||||
else {
|
||||
buffer.append( "?" );
|
||||
}
|
||||
buffer.append( "?" );
|
||||
sep = ',';
|
||||
builder.addParameterRegistration( registration );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue