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;
|
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 );
|
||||||
|
|
|
@ -54,7 +54,7 @@ import org.hibernate.mapping.UserDefinedType;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
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.procedure.spi.CallableStatementSupport;
|
||||||
import org.hibernate.query.SemanticException;
|
import org.hibernate.query.SemanticException;
|
||||||
import org.hibernate.query.spi.QueryOptions;
|
import org.hibernate.query.spi.QueryOptions;
|
||||||
|
@ -1314,7 +1314,7 @@ public class OracleDialect extends Dialect {
|
||||||
@Override
|
@Override
|
||||||
public CallableStatementSupport getCallableStatementSupport() {
|
public CallableStatementSupport getCallableStatementSupport() {
|
||||||
// Oracle supports returning cursors
|
// Oracle supports returning cursors
|
||||||
return StandardCallableStatementSupport.REF_CURSOR_INSTANCE;
|
return OracleCallableStatementSupport.REF_CURSOR_INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -54,6 +54,8 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
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.CastType;
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
import org.hibernate.query.sqm.IntervalType;
|
import org.hibernate.query.sqm.IntervalType;
|
||||||
|
@ -1122,7 +1124,6 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) {
|
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) {
|
||||||
// Not sure if it's a JDBC driver issue, but it doesn't work
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1168,4 +1169,10 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
public boolean supportsFromClauseInUpdate() {
|
public boolean supportsFromClauseInUpdate() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CallableStatementSupport getCallableStatementSupport() {
|
||||||
|
return SQLServerCallableStatementSupport.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,8 +84,8 @@ public class DB2CallableStatementSupport extends AbstractStandardCallableStateme
|
||||||
i + offset,
|
i + offset,
|
||||||
procedureCall
|
procedureCall
|
||||||
);
|
);
|
||||||
if ( registration.getName() != null ) {
|
if ( parameter.getName() != null ) {
|
||||||
buffer.append( ':' ).append( registration.getName() );
|
buffer.append( ':' ).append( parameter.getName() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
buffer.append( "?" );
|
buffer.append( "?" );
|
||||||
|
|
|
@ -69,8 +69,8 @@ public class JTDSCallableStatementSupport extends AbstractStandardCallableStatem
|
||||||
i + offset,
|
i + offset,
|
||||||
procedureCall
|
procedureCall
|
||||||
);
|
);
|
||||||
if ( registration.getName() != null ) {
|
if ( parameter.getName() != null ) {
|
||||||
buffer.append( '@' ).append( registration.getName() ).append( "=?" );
|
buffer.append( '@' ).append( parameter.getName() ).append( "=?" );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
buffer.append( "?" );
|
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 OutputableType<?> type = registration.getParameterType();
|
||||||
final String castType;
|
final String castType;
|
||||||
|
if ( parameter.getName() != null ) {
|
||||||
|
buffer.append( parameter.getName() ).append( " => " );
|
||||||
|
}
|
||||||
if ( type != null && type.getJdbcType() instanceof AbstractPostgreSQLStructJdbcType ) {
|
if ( type != null && type.getJdbcType() instanceof AbstractPostgreSQLStructJdbcType ) {
|
||||||
// We have to cast struct type parameters so that PostgreSQL understands nulls
|
// We have to cast struct type parameters so that PostgreSQL understands nulls
|
||||||
castType = ( (AbstractPostgreSQLStructJdbcType) type.getJdbcType() ).getStructTypeName();
|
castType = ( (AbstractPostgreSQLStructJdbcType) type.getJdbcType() ).getStructTypeName();
|
||||||
|
@ -164,12 +167,7 @@ public class PostgreSQLCallableStatementSupport extends AbstractStandardCallable
|
||||||
else {
|
else {
|
||||||
castType = null;
|
castType = null;
|
||||||
}
|
}
|
||||||
if ( registration.getName() != null ) {
|
|
||||||
buffer.append( ':' ).append( registration.getName() );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
buffer.append( "?" );
|
buffer.append( "?" );
|
||||||
}
|
|
||||||
if ( castType != null ) {
|
if ( castType != null ) {
|
||||||
buffer.append( " as " ).append( castType ).append( ')' );
|
buffer.append( " as " ).append( castType ).append( ')' );
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
package org.hibernate.procedure.internal;
|
package org.hibernate.procedure.internal;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.DatabaseMetaData;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -144,36 +145,38 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
||||||
);
|
);
|
||||||
|
|
||||||
final String jdbcParamName;
|
final String jdbcParamName;
|
||||||
if ( isNamed && canDoNameParameterBinding( typeToUse, procedureCall ) ) {
|
|
||||||
jdbcParamName = this.name;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
jdbcParamName = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
||||||
|
.getFactory()
|
||||||
|
.getJdbcServices()
|
||||||
|
.getJdbcEnvironment()
|
||||||
|
.getExtractedDatabaseMetaData();
|
||||||
|
|
||||||
switch ( mode ) {
|
switch ( mode ) {
|
||||||
case REF_CURSOR:
|
case REF_CURSOR:
|
||||||
|
jdbcParamName = this.name != null && databaseMetaData.supportsNamedParameters() ? this.name : null;
|
||||||
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( jdbcParamName, startIndex );
|
refCursorExtractor = new JdbcCallRefCursorExtractorImpl( jdbcParamName, startIndex );
|
||||||
parameterBinder = null;
|
parameterBinder = null;
|
||||||
parameterExtractor = null;
|
parameterExtractor = null;
|
||||||
break;
|
break;
|
||||||
case IN:
|
case IN:
|
||||||
|
jdbcParamName = getJdbcParamName( procedureCall, isNamed, 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 );
|
||||||
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 );
|
||||||
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 );
|
||||||
|
@ -184,6 +187,14 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
||||||
return new JdbcCallParameterRegistrationImpl( jdbcParamName, startIndex, mode, typeToUse, parameterBinder, parameterExtractor, refCursorExtractor );
|
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) {
|
private void validateBindableType(BindableType<T> bindableType, int startIndex) {
|
||||||
if ( bindableType == null ) {
|
if ( bindableType == null ) {
|
||||||
throw new ParameterTypeException(
|
throw new ParameterTypeException(
|
||||||
|
@ -242,12 +253,8 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
||||||
|
|
||||||
private boolean canDoNameParameterBinding(
|
private boolean canDoNameParameterBinding(
|
||||||
BindableType<?> hibernateType,
|
BindableType<?> hibernateType,
|
||||||
ProcedureCallImplementor<?> procedureCall) {
|
ProcedureCallImplementor<?> procedureCall,
|
||||||
final ExtractedDatabaseMetaData databaseMetaData = procedureCall.getSession()
|
ExtractedDatabaseMetaData databaseMetaData) {
|
||||||
.getFactory()
|
|
||||||
.getJdbcServices()
|
|
||||||
.getJdbcEnvironment()
|
|
||||||
.getExtractedDatabaseMetaData();
|
|
||||||
return procedureCall.getFunctionReturn() == null
|
return procedureCall.getFunctionReturn() == null
|
||||||
&& databaseMetaData.supportsNamedParameters()
|
&& databaseMetaData.supportsNamedParameters()
|
||||||
&& hibernateType instanceof ProcedureParameterNamedBinder
|
&& 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,
|
i + offset,
|
||||||
procedureCall
|
procedureCall
|
||||||
);
|
);
|
||||||
if ( registration.getName() != null ) {
|
if ( parameter.getName() != null ) {
|
||||||
buffer.append( ':' ).append( registration.getName() );
|
appendNameParameter( buffer, parameter, registration );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
buffer.append( "?" );
|
buffer.append( "?" );
|
||||||
|
@ -108,6 +108,13 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
|
||||||
return builder.buildJdbcCall();
|
return builder.buildJdbcCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void appendNameParameter(
|
||||||
|
StringBuilder buffer,
|
||||||
|
ProcedureParameterImplementor parameter,
|
||||||
|
JdbcCallParameterRegistration registration) {
|
||||||
|
buffer.append( '?' );
|
||||||
|
}
|
||||||
|
|
||||||
private void verifyRefCursorSupport(Dialect dialect) {
|
private void verifyRefCursorSupport(Dialect dialect) {
|
||||||
if ( ! supportsRefCursors ) {
|
if ( ! supportsRefCursors ) {
|
||||||
throw new QueryException( "Dialect [" + dialect.getClass().getName() + "] not known to support REF_CURSOR parameters" );
|
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,
|
i + offset,
|
||||||
procedureCall
|
procedureCall
|
||||||
);
|
);
|
||||||
if ( registration.getName() != null ) {
|
if ( parameter.getName() != null ) {
|
||||||
throw new QueryException( "The JDBC driver does not support named parameters" );
|
buffer.append("@").append( parameter.getName() ).append( " = ?" );
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
buffer.append( "?" );
|
buffer.append( "?" );
|
||||||
|
}
|
||||||
sep = ',';
|
sep = ',';
|
||||||
builder.addParameterRegistration( registration );
|
builder.addParameterRegistration( registration );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue