HHH-7914 - Improve new stored procedure call support

This commit is contained in:
Steve Ebersole 2013-01-11 17:47:29 -06:00
parent 0713cea180
commit 5de1677ce7
29 changed files with 1746 additions and 946 deletions

View File

@ -25,6 +25,8 @@ package org.hibernate;
import java.io.Serializable;
import org.hibernate.procedure.Call;
/**
* Contract methods shared between {@link Session} and {@link StatelessSession}
*
@ -91,17 +93,18 @@ public interface SharedSessionContract extends Serializable {
*
* @return The representation of the procedure call.
*/
public StoredProcedureCall createStoredProcedureCall(String procedureName);
public Call createStoredProcedureCall(String procedureName);
/**
* Creates a call to a stored procedure with specific result set entity mappings
* Creates a call to a stored procedure with specific result set entity mappings. Each class named
* is considered a "root return".
*
* @param procedureName The name of the procedure.
* @param resultClasses The entity(s) to map the result on to.
*
* @return The representation of the procedure call.
*/
public StoredProcedureCall createStoredProcedureCall(String procedureName, Class... resultClasses);
public Call createStoredProcedureCall(String procedureName, Class... resultClasses);
/**
* Creates a call to a stored procedure with specific result set entity mappings
@ -111,7 +114,7 @@ public interface SharedSessionContract extends Serializable {
*
* @return The representation of the procedure call.
*/
public StoredProcedureCall createStoredProcedureCall(String procedureName, String... resultSetMappings);
public Call createStoredProcedureCall(String procedureName, String... resultSetMappings);
/**
* Create {@link Criteria} instance for the given class (entity or subclasses/implementors)

View File

@ -1,198 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate;
import javax.persistence.ParameterMode;
import javax.persistence.TemporalType;
import java.util.List;
import org.hibernate.internal.StoredProcedureCallImpl;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public interface StoredProcedureCall extends BasicQueryContract, SynchronizeableQuery {
@Override
StoredProcedureCall addSynchronizedQuerySpace(String querySpace);
@Override
StoredProcedureCall addSynchronizedEntityName(String entityName) throws MappingException;
@Override
StoredProcedureCall addSynchronizedEntityClass(Class entityClass) throws MappingException;
/**
* Get the name of the stored procedure to be called.
*
* @return The procedure name.
*/
public String getProcedureName();
/**
* Register a positional parameter.
* All positional parameters must be registered.
*
* @param position parameter position
* @param type type of the parameter
* @param mode parameter mode
*
* @return the same query instance
*/
StoredProcedureCall registerStoredProcedureParameter(
int position,
Class type,
ParameterMode mode);
/**
* Register a named parameter.
* When using parameter names, all parameters must be registered
* in the order in which they occur in the parameter list of the
* stored procedure.
*
* @param parameterName name of the parameter as registered or
* <p/>
* specified in metadata
* @param type type of the parameter
* @param mode parameter mode
*
* @return the same query instance
*/
StoredProcedureCall registerStoredProcedureParameter(
String parameterName,
Class type,
ParameterMode mode);
/**
* Retrieve all registered parameters.
*
* @return The (immutable) list of all registered parameters.
*/
public List<StoredProcedureParameter> getRegisteredParameters();
/**
* Retrieve parameter registered by name.
*
* @param name The name under which the parameter of interest was registered.
*
* @return The registered parameter.
*/
public StoredProcedureParameter getRegisteredParameter(String name);
public StoredProcedureParameter getRegisteredParameter(int position);
public StoredProcedureOutputs getOutputs();
/**
* Describes a parameter registered with the stored procedure. Parameters can be either named or positional
* as the registration mechanism. Named and positional should not be mixed.
*/
public static interface StoredProcedureParameter<T> {
/**
* The name under which this parameter was registered. Can be {@code null} which should indicate that
* positional registration was used (and therefore {@link #getPosition()} should return non-null.
*
* @return The name;
*/
public String getName();
/**
* The position at which this parameter was registered. Can be {@code null} which should indicate that
* named registration was used (and therefore {@link #getName()} should return non-null.
*
* @return The name;
*/
public Integer getPosition();
/**
* Obtain the Java type of parameter. This is used to guess the Hibernate type (unless {@link #setHibernateType}
* is called explicitly).
*
* @return The parameter Java type.
*/
public Class<T> getType();
/**
* Retrieves the parameter "mode" which describes how the parameter is defined in the actual database procedure
* definition (is it an INPUT parameter? An OUTPUT parameter? etc).
*
* @return The parameter mode.
*/
public ParameterMode getMode();
/**
* Set the Hibernate mapping type for this parameter.
*
* @param type The Hibernate mapping type.
*/
public void setHibernateType(Type type);
/**
* Retrieve the binding associated with this parameter. The binding is only relevant for INPUT parameters. Can
* return {@code null} if nothing has been bound yet. To bind a value to the parameter use one of the
* {@link #bindValue} methods.
*
* @return The parameter binding
*/
public StoredProcedureParameterBind getParameterBind();
/**
* Bind a value to the parameter. How this value is bound to the underlying JDBC CallableStatement is
* totally dependent on the Hibernate type.
*
* @param value The value to bind.
*/
public void bindValue(T value);
/**
* Bind a value to the parameter, using just a specified portion of the DATE/TIME value. It is illegal to call
* this form if the parameter is not DATE/TIME type. The Hibernate type is circumvented in this case and
* an appropriate "precision" Type is used instead.
*
* @param value The value to bind
* @param explicitTemporalType An explicitly supplied TemporalType.
*/
public void bindValue(T value, TemporalType explicitTemporalType);
}
/**
* Describes an input value binding for any IN/INOUT parameters.
*/
public static interface StoredProcedureParameterBind<T> {
/**
* Retrieves the bound value.
*
* @return The bound value.
*/
public T getValue();
/**
* If {@code <T>} represents a DATE/TIME type value, JPA usually allows specifying the particular parts of
* the DATE/TIME value to be bound. This value represents the particular part the user requested to be bound.
*
* @return The explicitly supplied TemporalType.
*/
public TemporalType getExplicitTemporalType();
}
}

View File

@ -37,7 +37,7 @@ import org.hibernate.SQLQuery;
import org.hibernate.ScrollableResults;
import org.hibernate.SessionException;
import org.hibernate.SharedSessionContract;
import org.hibernate.StoredProcedureCall;
import org.hibernate.procedure.Call;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
@ -59,6 +59,7 @@ import org.hibernate.jdbc.WorkExecutorVisitable;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.procedure.internal.CallImpl;
import org.hibernate.type.Type;
/**
@ -240,27 +241,27 @@ public abstract class AbstractSessionImpl implements Serializable, SharedSession
@Override
@SuppressWarnings("UnnecessaryLocalVariable")
public StoredProcedureCall createStoredProcedureCall(String procedureName) {
public Call createStoredProcedureCall(String procedureName) {
errorIfClosed();
final StoredProcedureCall call = new StoredProcedureCallImpl( this, procedureName );
final Call call = new CallImpl( this, procedureName );
// call.setComment( "Dynamic stored procedure call" );
return call;
}
@Override
@SuppressWarnings("UnnecessaryLocalVariable")
public StoredProcedureCall createStoredProcedureCall(String procedureName, Class... resultClasses) {
public Call createStoredProcedureCall(String procedureName, Class... resultClasses) {
errorIfClosed();
final StoredProcedureCall call = new StoredProcedureCallImpl( this, procedureName, resultClasses );
final Call call = new CallImpl( this, procedureName, resultClasses );
// call.setComment( "Dynamic stored procedure call" );
return call;
}
@Override
@SuppressWarnings("UnnecessaryLocalVariable")
public StoredProcedureCall createStoredProcedureCall(String procedureName, String... resultSetMappings) {
public Call createStoredProcedureCall(String procedureName, String... resultSetMappings) {
errorIfClosed();
final StoredProcedureCall call = new StoredProcedureCallImpl( this, procedureName, resultSetMappings );
final Call call = new CallImpl( this, procedureName, resultSetMappings );
// call.setComment( "Dynamic stored procedure call" );
return call;
}

View File

@ -76,7 +76,7 @@ import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.SessionException;
import org.hibernate.StoredProcedureCall;
import org.hibernate.procedure.Call;
import org.hibernate.engine.spi.SessionOwner;
import org.hibernate.SharedSessionBuilder;
import org.hibernate.SimpleNaturalIdLoadAccess;
@ -141,7 +141,6 @@ import org.hibernate.event.spi.ResolveNaturalIdEventListener;
import org.hibernate.event.spi.SaveOrUpdateEvent;
import org.hibernate.event.spi.SaveOrUpdateEventListener;
import org.hibernate.internal.CriteriaImpl.CriterionEntry;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.jdbc.WorkExecutor;
@ -1743,21 +1742,21 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
}
@Override
public StoredProcedureCall createStoredProcedureCall(String procedureName) {
public Call createStoredProcedureCall(String procedureName) {
errorIfClosed();
checkTransactionSynchStatus();
return super.createStoredProcedureCall( procedureName );
}
@Override
public StoredProcedureCall createStoredProcedureCall(String procedureName, String... resultSetMappings) {
public Call createStoredProcedureCall(String procedureName, String... resultSetMappings) {
errorIfClosed();
checkTransactionSynchStatus();
return super.createStoredProcedureCall( procedureName, resultSetMappings );
}
@Override
public StoredProcedureCall createStoredProcedureCall(String procedureName, Class... resultClasses) {
public Call createStoredProcedureCall(String procedureName, Class... resultClasses) {
errorIfClosed();
checkTransactionSynchStatus();
return super.createStoredProcedureCall( procedureName, resultClasses );

View File

@ -1,608 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.internal;
import javax.persistence.ParameterMode;
import javax.persistence.TemporalType;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.StoredProcedureCall;
import org.hibernate.StoredProcedureOutputs;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.jdbc.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
import org.hibernate.type.DateType;
import org.hibernate.type.ProcedureParameterExtractionAware;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public class StoredProcedureCallImpl extends AbstractBasicQueryContractImpl implements StoredProcedureCall {
private static final Logger log = Logger.getLogger( StoredProcedureCallImpl.class );
private final String procedureName;
private final NativeSQLQueryReturn[] queryReturns;
private TypeOfParameter typeOfParameters = TypeOfParameter.UNKNOWN;
private List<StoredProcedureParameterImplementor> registeredParameters = new ArrayList<StoredProcedureParameterImplementor>();
private Set<String> synchronizedQuerySpaces;
@SuppressWarnings("unchecked")
public StoredProcedureCallImpl(SessionImplementor session, String procedureName) {
this( session, procedureName, (List) null );
}
public StoredProcedureCallImpl(SessionImplementor session, String procedureName, List<NativeSQLQueryReturn> queryReturns) {
super( session );
this.procedureName = procedureName;
if ( queryReturns == null || queryReturns.isEmpty() ) {
this.queryReturns = new NativeSQLQueryReturn[0];
}
else {
this.queryReturns = queryReturns.toArray( new NativeSQLQueryReturn[ queryReturns.size() ] );
}
}
public StoredProcedureCallImpl(SessionImplementor session, String procedureName, Class... resultClasses) {
this( session, procedureName, collectQueryReturns( resultClasses ) );
}
private static List<NativeSQLQueryReturn> collectQueryReturns(Class[] resultClasses) {
if ( resultClasses == null || resultClasses.length == 0 ) {
return null;
}
List<NativeSQLQueryReturn> queryReturns = new ArrayList<NativeSQLQueryReturn>( resultClasses.length );
int i = 1;
for ( Class resultClass : resultClasses ) {
queryReturns.add( new NativeSQLQueryRootReturn( "alias" + i, resultClass.getName(), LockMode.READ ) );
i++;
}
return queryReturns;
}
public StoredProcedureCallImpl(SessionImplementor session, String procedureName, String... resultSetMappings) {
this( session, procedureName, collectQueryReturns( session, resultSetMappings ) );
}
private static List<NativeSQLQueryReturn> collectQueryReturns(SessionImplementor session, String[] resultSetMappings) {
if ( resultSetMappings == null || resultSetMappings.length == 0 ) {
return null;
}
List<NativeSQLQueryReturn> queryReturns = new ArrayList<NativeSQLQueryReturn>( resultSetMappings.length );
for ( String resultSetMapping : resultSetMappings ) {
ResultSetMappingDefinition mapping = session.getFactory().getResultSetMapping( resultSetMapping );
if ( mapping == null ) {
throw new MappingException( "Unknown SqlResultSetMapping [" + resultSetMapping + "]" );
}
queryReturns.addAll( Arrays.asList( mapping.getQueryReturns() ) );
}
return queryReturns;
}
// public StoredProcedureCallImpl(
// SessionImplementor session,
// String procedureName,
// List<StoredProcedureParameter> parameters) {
// // this form is intended for named stored procedure calls.
// // todo : introduce a NamedProcedureCallDefinition object to hold all needed info and pass that in here; will help with EM.addNamedQuery as well..
// this( session, procedureName );
// for ( StoredProcedureParameter parameter : parameters ) {
// registerParameter( (StoredProcedureParameterImplementor) parameter );
// }
// }
@Override
public String getProcedureName() {
return procedureName;
}
NativeSQLQueryReturn[] getQueryReturns() {
return queryReturns;
}
@Override
@SuppressWarnings("unchecked")
public StoredProcedureCall registerStoredProcedureParameter(int position, Class type, ParameterMode mode) {
registerParameter( new PositionalStoredProcedureParameter( this, position, mode, type ) );
return this;
}
private void registerParameter(StoredProcedureParameterImplementor parameter) {
if ( StringHelper.isNotEmpty( parameter.getName() ) ) {
prepareForNamedParameters();
}
else if ( parameter.getPosition() != null ) {
prepareForPositionalParameters();
}
else {
throw new IllegalArgumentException( "Given parameter did not define name nor position [" + parameter + "]" );
}
registeredParameters.add( parameter );
}
private void prepareForPositionalParameters() {
if ( typeOfParameters == TypeOfParameter.NAMED ) {
throw new QueryException( "Cannot mix named and positional parameters" );
}
typeOfParameters = TypeOfParameter.POSITIONAL;
}
private void prepareForNamedParameters() {
if ( typeOfParameters == TypeOfParameter.POSITIONAL ) {
throw new QueryException( "Cannot mix named and positional parameters" );
}
if ( typeOfParameters == null ) {
// protect to only do this check once
final ExtractedDatabaseMetaData databaseMetaData = session().getTransactionCoordinator()
.getJdbcCoordinator()
.getLogicalConnection()
.getJdbcServices()
.getExtractedMetaDataSupport();
if ( ! databaseMetaData.supportsNamedParameters() ) {
throw new QueryException(
"Named stored procedure parameters used, but JDBC driver does not support named parameters"
);
}
typeOfParameters = TypeOfParameter.NAMED;
}
}
@Override
@SuppressWarnings("unchecked")
public StoredProcedureCall registerStoredProcedureParameter(String name, Class type, ParameterMode mode) {
registerParameter( new NamedStoredProcedureParameter( this, name, mode, type ) );
return this;
}
@Override
@SuppressWarnings("unchecked")
public List<StoredProcedureParameter> getRegisteredParameters() {
return new ArrayList<StoredProcedureParameter>( registeredParameters );
}
@Override
public StoredProcedureParameterImplementor getRegisteredParameter(String name) {
if ( typeOfParameters != TypeOfParameter.NAMED ) {
throw new IllegalArgumentException( "Names were not used to register parameters with this stored procedure call" );
}
for ( StoredProcedureParameterImplementor parameter : registeredParameters ) {
if ( name.equals( parameter.getName() ) ) {
return parameter;
}
}
throw new IllegalArgumentException( "Could not locate parameter registered under that name [" + name + "]" );
}
@Override
public StoredProcedureParameterImplementor getRegisteredParameter(int position) {
try {
return registeredParameters.get( position );
}
catch ( Exception e ) {
throw new QueryException( "Could not locate parameter registered using that position [" + position + "]" );
}
}
@Override
public StoredProcedureOutputs getOutputs() {
// todo : going to need a very specialized Loader for this.
// or, might be a good time to look at splitting Loader up into:
// 1) building statement objects
// 2) executing statement objects
// 3) processing result sets
// for now assume there are no resultClasses nor mappings defined..
// TOTAL PROOF-OF-CONCEPT!!!!!!
final StringBuilder buffer = new StringBuilder().append( "{call " )
.append( procedureName )
.append( "(" );
String sep = "";
for ( StoredProcedureParameterImplementor parameter : registeredParameters ) {
for ( int i = 0; i < parameter.getSqlTypes().length; i++ ) {
buffer.append( sep ).append( "?" );
sep = ",";
}
}
buffer.append( ")}" );
try {
final CallableStatement statement = session().getTransactionCoordinator()
.getJdbcCoordinator()
.getLogicalConnection()
.getShareableConnectionProxy()
.prepareCall( buffer.toString() );
// prepare parameters
int i = 1;
for ( StoredProcedureParameterImplementor parameter : registeredParameters ) {
if ( parameter == null ) {
throw new QueryException( "Registered stored procedure parameters had gaps" );
}
parameter.prepare( statement, i );
i += parameter.getSqlTypes().length;
}
return new StoredProcedureOutputsImpl( this, statement );
}
catch (SQLException e) {
throw session().getFactory().getSQLExceptionHelper().convert(
e,
"Error preparing CallableStatement",
getProcedureName()
);
}
}
@Override
public Type[] getReturnTypes() throws HibernateException {
throw new NotYetImplementedException();
}
protected Set<String> synchronizedQuerySpaces() {
if ( synchronizedQuerySpaces == null ) {
synchronizedQuerySpaces = new HashSet<String>();
}
return synchronizedQuerySpaces;
}
@Override
@SuppressWarnings("unchecked")
public Collection<String> getSynchronizedQuerySpaces() {
if ( synchronizedQuerySpaces == null ) {
return Collections.emptySet();
}
else {
return Collections.unmodifiableSet( synchronizedQuerySpaces );
}
}
public Set<String> getSynchronizedQuerySpacesSet() {
return (Set<String>) getSynchronizedQuerySpaces();
}
@Override
public StoredProcedureCallImpl addSynchronizedQuerySpace(String querySpace) {
synchronizedQuerySpaces().add( querySpace );
return this;
}
@Override
public StoredProcedureCallImpl addSynchronizedEntityName(String entityName) {
addSynchronizedQuerySpaces( session().getFactory().getEntityPersister( entityName ) );
return this;
}
protected void addSynchronizedQuerySpaces(EntityPersister persister) {
synchronizedQuerySpaces().addAll( Arrays.asList( (String[]) persister.getQuerySpaces() ) );
}
@Override
public StoredProcedureCallImpl addSynchronizedEntityClass(Class entityClass) {
addSynchronizedQuerySpaces( session().getFactory().getEntityPersister( entityClass.getName() ) );
return this;
}
public QueryParameters buildQueryParametersObject() {
QueryParameters qp = super.buildQueryParametersObject();
// both of these are for documentation purposes, they are actually handled directly...
qp.setAutoDiscoverScalarTypes( true );
qp.setCallable( true );
return qp;
}
public StoredProcedureParameterImplementor[] collectRefCursorParameters() {
List<StoredProcedureParameterImplementor> refCursorParams = new ArrayList<StoredProcedureParameterImplementor>();
for ( StoredProcedureParameterImplementor param : registeredParameters ) {
if ( param.getMode() == ParameterMode.REF_CURSOR ) {
refCursorParams.add( param );
}
}
return refCursorParams.toArray( new StoredProcedureParameterImplementor[refCursorParams.size()] );
}
/**
* Ternary logic enum
*/
private static enum TypeOfParameter {
NAMED,
POSITIONAL,
UNKNOWN
}
protected static interface StoredProcedureParameterImplementor<T> extends StoredProcedureParameter<T> {
public void prepare(CallableStatement statement, int i) throws SQLException;
public int[] getSqlTypes();
public T extract(CallableStatement statement);
}
public static abstract class AbstractStoredProcedureParameterImpl<T> implements StoredProcedureParameterImplementor<T> {
private final StoredProcedureCallImpl procedureCall;
private final ParameterMode mode;
private final Class<T> type;
private int startIndex;
private Type hibernateType;
private int[] sqlTypes;
private StoredProcedureParameterBindImpl bind;
protected AbstractStoredProcedureParameterImpl(
StoredProcedureCallImpl procedureCall,
ParameterMode mode,
Class<T> type) {
this.procedureCall = procedureCall;
this.mode = mode;
this.type = type;
setHibernateType( session().getFactory().getTypeResolver().heuristicType( type.getName() ) );
}
@Override
public String getName() {
return null;
}
@Override
public Integer getPosition() {
return null;
}
@Override
public Class<T> getType() {
return type;
}
@Override
public ParameterMode getMode() {
return mode;
}
@Override
public void setHibernateType(Type type) {
if ( type == null ) {
throw new IllegalArgumentException( "Type cannot be null" );
}
this.hibernateType = type;
this.sqlTypes = hibernateType.sqlTypes( session().getFactory() );
}
protected SessionImplementor session() {
return procedureCall.session();
}
@Override
public void prepare(CallableStatement statement, int startIndex) throws SQLException {
if ( mode == ParameterMode.REF_CURSOR ) {
throw new NotYetImplementedException( "Support for REF_CURSOR parameters not yet supported" );
}
this.startIndex = startIndex;
if ( mode == ParameterMode.IN || mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) {
if ( mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) {
if ( sqlTypes.length > 1 ) {
if ( ProcedureParameterExtractionAware.class.isInstance( hibernateType )
&& ( (ProcedureParameterExtractionAware) hibernateType ).canDoExtraction() ) {
// the type can handle multi-param extraction...
}
else {
// it cannot...
throw new UnsupportedOperationException(
"Type [" + hibernateType + "] does support multi-parameter value extraction"
);
}
}
for ( int i = 0; i < sqlTypes.length; i++ ) {
statement.registerOutParameter( startIndex + i, sqlTypes[i] );
}
}
if ( mode == ParameterMode.INOUT || mode == ParameterMode.IN ) {
if ( bind == null || bind.getValue() == null ) {
log.debugf(
"Stored procedure [%s] IN/INOUT parameter [%s] not bound; assuming procedure defines default value",
procedureCall.getProcedureName(),
this
);
}
else {
final Type typeToUse;
if ( bind.getExplicitTemporalType() != null && bind.getExplicitTemporalType() == TemporalType.TIMESTAMP ) {
typeToUse = hibernateType;
}
else if ( bind.getExplicitTemporalType() != null && bind.getExplicitTemporalType() == TemporalType.DATE ) {
typeToUse = DateType.INSTANCE;
}
else {
typeToUse = hibernateType;
}
typeToUse.nullSafeSet( statement, bind.getValue(), startIndex, session() );
}
}
}
else {
// we have a REF_CURSOR type param
if ( procedureCall.typeOfParameters == TypeOfParameter.NAMED ) {
session().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class )
.registerRefCursorParameter( statement, getName() );
}
else {
session().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class )
.registerRefCursorParameter( statement, getPosition() );
}
}
}
public int[] getSqlTypes() {
return sqlTypes;
}
@Override
public StoredProcedureParameterBind getParameterBind() {
return bind;
}
@Override
public void bindValue(T value) {
this.bind = new StoredProcedureParameterBindImpl<T>( value );
}
@Override
public void bindValue(T value, TemporalType explicitTemporalType) {
if ( explicitTemporalType != null ) {
if ( ! isDateTimeType() ) {
throw new IllegalArgumentException( "TemporalType should not be specified for non date/time type" );
}
}
this.bind = new StoredProcedureParameterBindImpl<T>( value, explicitTemporalType );
}
private boolean isDateTimeType() {
return Date.class.isAssignableFrom( type )
|| Calendar.class.isAssignableFrom( type );
}
@Override
@SuppressWarnings("unchecked")
public T extract(CallableStatement statement) {
if ( mode == ParameterMode.IN ) {
throw new QueryException( "IN parameter not valid for output extraction" );
}
else if ( mode == ParameterMode.REF_CURSOR ) {
throw new QueryException( "REF_CURSOR parameters should be accessed via results" );
}
try {
if ( ProcedureParameterExtractionAware.class.isInstance( hibernateType ) ) {
return (T) ( (ProcedureParameterExtractionAware) hibernateType ).extract( statement, startIndex, session() );
}
else {
return (T) statement.getObject( startIndex );
}
}
catch (SQLException e) {
throw procedureCall.session().getFactory().getSQLExceptionHelper().convert(
e,
"Unable to extract OUT/INOUT parameter value"
);
}
}
}
public static class StoredProcedureParameterBindImpl<T> implements StoredProcedureParameterBind<T> {
private final T value;
private final TemporalType explicitTemporalType;
public StoredProcedureParameterBindImpl(T value) {
this( value, null );
}
public StoredProcedureParameterBindImpl(T value, TemporalType explicitTemporalType) {
this.value = value;
this.explicitTemporalType = explicitTemporalType;
}
@Override
public T getValue() {
return value;
}
@Override
public TemporalType getExplicitTemporalType() {
return explicitTemporalType;
}
}
public static class NamedStoredProcedureParameter<T> extends AbstractStoredProcedureParameterImpl<T> {
private final String name;
public NamedStoredProcedureParameter(
StoredProcedureCallImpl procedureCall,
String name,
ParameterMode mode,
Class<T> type) {
super( procedureCall, mode, type );
this.name = name;
}
@Override
public String getName() {
return name;
}
}
public static class PositionalStoredProcedureParameter<T> extends AbstractStoredProcedureParameterImpl<T> {
private final Integer position;
public PositionalStoredProcedureParameter(
StoredProcedureCallImpl procedureCall,
Integer position,
ParameterMode mode,
Class<T> type) {
super( procedureCall, mode, type );
this.position = position;
}
@Override
public Integer getPosition() {
return position;
}
}
}

View File

@ -0,0 +1,136 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
import javax.persistence.ParameterMode;
import java.util.List;
import org.hibernate.BasicQueryContract;
import org.hibernate.MappingException;
import org.hibernate.SynchronizeableQuery;
/**
* Defines support for executing database stored procedures and functions
*
* @author Steve Ebersole
*/
public interface Call extends BasicQueryContract, SynchronizeableQuery {
@Override
public Call addSynchronizedQuerySpace(String querySpace);
@Override
public Call addSynchronizedEntityName(String entityName) throws MappingException;
@Override
public Call addSynchronizedEntityClass(Class entityClass) throws MappingException;
/**
* Get the name of the stored procedure to be called.
*
* @return The procedure name.
*/
public String getProcedureName();
/**
* Basic form for registering a positional parameter.
*
* @param position The position
* @param type The Java type of the parameter
* @param mode The parameter mode (in, out, inout)
*
* @return The parameter registration memento
*/
public <T> ParameterRegistration<T> registerParameter(int position, Class<T> type, ParameterMode mode);
/**
* Chained form of {@link #registerParameter(int, Class, javax.persistence.ParameterMode)}
*
* @param position The position
* @param type The Java type of the parameter
* @param mode The parameter mode (in, out, inout)
*
* @return {@code this}, for method chaining
*/
public Call registerParameter0(int position, Class type, ParameterMode mode);
/**
* Retrieve a previously registered parameter memento by the position under which it was registered.
*
* @param position The parameter position
*
* @return The parameter registration memento
*/
public ParameterRegistration getParameterRegistration(int position);
/**
* Basic form for registering a named parameter.
*
* @param parameterName The parameter name
* @param type The Java type of the parameter
* @param mode The parameter mode (in, out, inout)
*
* @return The parameter registration memento
*/
public <T> ParameterRegistration<T> registerParameter(String parameterName, Class<T> type, ParameterMode mode)
throws NamedParametersNotSupportedException;
/**
* Chained form of {@link #registerParameter(String, Class, javax.persistence.ParameterMode)}
*
* @param parameterName The parameter name
* @param type The Java type of the parameter
* @param mode The parameter mode (in, out, inout)
*
* @return The parameter registration memento
*/
public Call registerParameter0(String parameterName, Class type, ParameterMode mode)
throws NamedParametersNotSupportedException;
/**
* Retrieve a previously registered parameter memento by the name under which it was registered.
*
* @param name The parameter name
*
* @return The parameter registration memento
*/
public ParameterRegistration getParameterRegistration(String name);
/**
* Retrieve all registered parameters.
*
* @return The (immutable) list of all registered parameters.
*/
public List<ParameterRegistration> getRegisteredParameters();
/**
* Retrieves access to outputs of this procedure call. Can be called multiple times, returning the same
* Output instance each time.
* <p/>
* Note that the procedure will not actually be executed until the outputs are actually accessed.
*
* @return The outputs representation
*/
public Outputs getOutputs();
}

View File

@ -0,0 +1,30 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
/**
* @author Steve Ebersole
*/
public interface InOutParameterRegistration<T> extends InParameterRegistration<T>, OutParameterRegistration<T> {
}

View File

@ -1,7 +1,7 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
@ -21,11 +21,10 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate;
package org.hibernate.procedure;
/**
* @author Steve Ebersole
*/
public interface StoredProcedureReturn {
public boolean isResultSet();
public interface InParameterRegistration<T> extends ParameterRegistration<T> {
}

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
import org.hibernate.HibernateException;
/**
* Thrown to indicate that an attempt was made to register a stored procedure named parameter, but the underlying
* database reports to not support named parameters.
*
* @author Steve Ebersole
*/
public class NamedParametersNotSupportedException extends HibernateException {
public NamedParametersNotSupportedException(String message) {
super( message );
}
}

View File

@ -1,7 +1,7 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
@ -21,11 +21,10 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate;
package org.hibernate.procedure;
/**
* @author Steve Ebersole
*/
public interface StoredProcedureUpdateCountReturn extends StoredProcedureReturn {
public int getUpdateCount();
public interface OutParameterRegistration<T> extends ParameterRegistration<T> {
}

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate;
package org.hibernate.procedure;
/**
* Represents all the outputs of a call to a database stored procedure (or function) through the JDBC
@ -29,7 +29,22 @@ package org.hibernate;
*
* @author Steve Ebersole
*/
public interface StoredProcedureOutputs {
public interface Outputs {
/**
* Retrieve the value of an OUTPUT parameter by the parameter's registration memento.
* <p/>
* Should NOT be called for parameters registered as REF_CURSOR. REF_CURSOR parameters should be
* accessed via the returns (see {@link #getNextReturn}
*
* @param parameterRegistration The parameter's registration memento.
*
* @return The output value.
*
* @see Call#registerParameter(String, Class, javax.persistence.ParameterMode)
*/
public <T> T getOutputParameterValue(ParameterRegistration<T> parameterRegistration);
/**
* Retrieve the value of an OUTPUT parameter by the name under which the parameter was registered.
*
@ -37,7 +52,7 @@ public interface StoredProcedureOutputs {
*
* @return The output value.
*
* @see StoredProcedureCall#registerStoredProcedureParameter(String, Class, javax.persistence.ParameterMode)
* @see Call#registerParameter(String, Class, javax.persistence.ParameterMode)
*/
public Object getOutputParameterValue(String name);
@ -48,7 +63,7 @@ public interface StoredProcedureOutputs {
*
* @return The output value.
*
* @see StoredProcedureCall#registerStoredProcedureParameter(int, Class, javax.persistence.ParameterMode)
* @see Call#registerParameter(int, Class, javax.persistence.ParameterMode)
*/
public Object getOutputParameterValue(int position);
@ -65,5 +80,5 @@ public interface StoredProcedureOutputs {
*
* @return The next return.
*/
public StoredProcedureReturn getNextReturn();
public Return getNextReturn();
}

View File

@ -0,0 +1,46 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
import javax.persistence.TemporalType;
/**
* Describes an input value binding for any IN/INOUT parameters.
*/
public interface ParameterBind<T> {
/**
* Retrieves the bound value.
*
* @return The bound value.
*/
public T getValue();
/**
* If {@code <T>} represents a DATE/TIME type value, JPA usually allows specifying the particular parts of
* the DATE/TIME value to be bound. This value represents the particular part the user requested to be bound.
*
* @return The explicitly supplied TemporalType.
*/
public TemporalType getExplicitTemporalType();
}

View File

@ -0,0 +1,37 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
import org.hibernate.HibernateException;
/**
* Thrown to indicate a misuse of a {@link ParameterRegistration}
*
* @author Steve Ebersole
*/
public class ParameterMisuseException extends HibernateException {
public ParameterMisuseException(String message) {
super( message );
}
}

View File

@ -0,0 +1,100 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
import javax.persistence.ParameterMode;
import javax.persistence.TemporalType;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public interface ParameterRegistration<T> {
/**
* The name under which this parameter was registered. Can be {@code null} which should indicate that
* positional registration was used (and therefore {@link #getPosition()} should return non-null.
*
* @return The name;
*/
public String getName();
/**
* The position at which this parameter was registered. Can be {@code null} which should indicate that
* named registration was used (and therefore {@link #getName()} should return non-null.
*
* @return The name;
*/
public Integer getPosition();
/**
* Obtain the Java type of parameter. This is used to guess the Hibernate type (unless {@link #setHibernateType}
* is called explicitly).
*
* @return The parameter Java type.
*/
public Class<T> getType();
/**
* Retrieves the parameter "mode" which describes how the parameter is defined in the actual database procedure
* definition (is it an INPUT parameter? An OUTPUT parameter? etc).
*
* @return The parameter mode.
*/
public ParameterMode getMode();
/**
* Set the Hibernate mapping type for this parameter.
*
* @param type The Hibernate mapping type.
*/
public void setHibernateType(Type type);
/**
* Retrieve the binding associated with this parameter. The binding is only relevant for INPUT parameters. Can
* return {@code null} if nothing has been bound yet. To bind a value to the parameter use one of the
* {@link #bindValue} methods.
*
* @return The parameter binding
*/
public ParameterBind getParameterBind();
/**
* Bind a value to the parameter. How this value is bound to the underlying JDBC CallableStatement is
* totally dependent on the Hibernate type.
*
* @param value The value to bind.
*/
public void bindValue(T value);
/**
* Bind a value to the parameter, using just a specified portion of the DATE/TIME value. It is illegal to call
* this form if the parameter is not DATE/TIME type. The Hibernate type is circumvented in this case and
* an appropriate "precision" Type is used instead.
*
* @param value The value to bind
* @param explicitTemporalType An explicitly supplied TemporalType.
*/
public void bindValue(T value, TemporalType explicitTemporalType);
}

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate;
package org.hibernate.procedure;
import java.util.List;
@ -30,7 +30,7 @@ import java.util.List;
*
* @author Steve Ebersole
*/
public interface StoredProcedureResultSetReturn extends StoredProcedureReturn {
public interface ResultSetReturn extends Return {
/**
* Consume the underlying {@link java.sql.ResultSet} and return the resulting List.
*

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
/**
* Common contract for procedure call results which can be either results ({@link ResultSetReturn}) or update
* counts ({@link UpdateCountReturn}).
*
* @author Steve Ebersole
*/
public interface Return {
/**
* Determine if this return is a result (castable to {@link ResultSetReturn}). The alternative is that it is
* an update count (castable to {@link UpdateCountReturn}).
*
* @return {@code true} indicates that {@code this} can be safely cast to {@link ResultSetReturn}), other wise
* it can be cast to {@link UpdateCountReturn}.
*/
public boolean isResultSet();
}

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
/**
* Models a stored procedure result that is a result set.
*
* @author Steve Ebersole
*/
public interface UpdateCountReturn extends Return {
/**
* Retrieve the associated update count
*
* @return The update count
*/
public int getUpdateCount();
}

View File

@ -0,0 +1,267 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure.internal;
import javax.persistence.ParameterMode;
import javax.persistence.TemporalType;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Date;
import org.jboss.logging.Logger;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.procedure.ParameterBind;
import org.hibernate.procedure.ParameterMisuseException;
import org.hibernate.type.DateType;
import org.hibernate.type.ProcedureParameterExtractionAware;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public abstract class AbstractParameterRegistrationImpl<T> implements ParameterRegistrationImplementor<T> {
private static final Logger log = Logger.getLogger( AbstractParameterRegistrationImpl.class );
private final CallImpl procedureCall;
private final Integer position;
private final String name;
private final ParameterMode mode;
private final Class<T> type;
private ParameterBindImpl bind;
private int startIndex;
private Type hibernateType;
private int[] sqlTypes;
protected AbstractParameterRegistrationImpl(
CallImpl procedureCall,
Integer position,
Class<T> type,
ParameterMode mode) {
this( procedureCall, position, null, type, mode );
}
protected AbstractParameterRegistrationImpl(
CallImpl procedureCall,
String name,
Class<T> type,
ParameterMode mode) {
this( procedureCall, null, name, type, mode );
}
private AbstractParameterRegistrationImpl(
CallImpl procedureCall,
Integer position,
String name,
Class<T> type,
ParameterMode mode) {
this.procedureCall = procedureCall;
this.position = position;
this.name = name;
this.mode = mode;
this.type = type;
setHibernateType( session().getFactory().getTypeResolver().heuristicType( type.getName() ) );
}
protected SessionImplementor session() {
return procedureCall.session();
}
@Override
public String getName() {
return name;
}
@Override
public Integer getPosition() {
return position;
}
@Override
public Class<T> getType() {
return type;
}
@Override
public ParameterMode getMode() {
return mode;
}
@Override
public void setHibernateType(Type type) {
if ( type == null ) {
throw new IllegalArgumentException( "Type cannot be null" );
}
this.hibernateType = type;
this.sqlTypes = hibernateType.sqlTypes( session().getFactory() );
}
@Override
public ParameterBind getParameterBind() {
return bind;
}
@Override
public void bindValue(T value) {
validateBindability();
this.bind = new ParameterBindImpl<T>( value );
}
private void validateBindability() {
if ( ! canBind() ) {
throw new ParameterMisuseException( "Cannot bind value to non-input parameter : " + this );
}
}
private boolean canBind() {
return mode == ParameterMode.IN || mode == ParameterMode.INOUT;
}
@Override
public void bindValue(T value, TemporalType explicitTemporalType) {
validateBindability();
if ( explicitTemporalType != null ) {
if ( ! isDateTimeType() ) {
throw new IllegalArgumentException( "TemporalType should not be specified for non date/time type" );
}
}
this.bind = new ParameterBindImpl<T>( value, explicitTemporalType );
}
private boolean isDateTimeType() {
return Date.class.isAssignableFrom( type )
|| Calendar.class.isAssignableFrom( type );
}
@Override
public void prepare(CallableStatement statement, int startIndex) throws SQLException {
if ( mode == ParameterMode.REF_CURSOR ) {
throw new NotYetImplementedException( "Support for REF_CURSOR parameters not yet supported" );
}
this.startIndex = startIndex;
if ( mode == ParameterMode.IN || mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) {
if ( mode == ParameterMode.INOUT || mode == ParameterMode.OUT ) {
if ( sqlTypes.length > 1 ) {
if ( ProcedureParameterExtractionAware.class.isInstance( hibernateType )
&& ( (ProcedureParameterExtractionAware) hibernateType ).canDoExtraction() ) {
// the type can handle multi-param extraction...
}
else {
// it cannot...
throw new UnsupportedOperationException(
"Type [" + hibernateType + "] does support multi-parameter value extraction"
);
}
}
for ( int i = 0; i < sqlTypes.length; i++ ) {
statement.registerOutParameter( startIndex + i, sqlTypes[i] );
}
}
if ( mode == ParameterMode.INOUT || mode == ParameterMode.IN ) {
if ( bind == null || bind.getValue() == null ) {
// the user did not bind a value to the parameter being processed. That might be ok *if* the
// procedure as defined in the database defines a default value for that parameter.
// Unfortunately there is not a way to reliably know through JDBC metadata whether a procedure
// parameter defines a default value. So we simply allow the procedure execution to happen
// assuming that the database will complain appropriately if not setting the given parameter
// bind value is an error.
log.debugf(
"Stored procedure [%s] IN/INOUT parameter [%s] not bound; assuming procedure defines default value",
procedureCall.getProcedureName(),
this
);
}
else {
final Type typeToUse;
if ( bind.getExplicitTemporalType() != null && bind.getExplicitTemporalType() == TemporalType.TIMESTAMP ) {
typeToUse = hibernateType;
}
else if ( bind.getExplicitTemporalType() != null && bind.getExplicitTemporalType() == TemporalType.DATE ) {
typeToUse = DateType.INSTANCE;
}
else {
typeToUse = hibernateType;
}
typeToUse.nullSafeSet( statement, bind.getValue(), startIndex, session() );
}
}
}
else {
// we have a REF_CURSOR type param
if ( procedureCall.getParameterStrategy() == ParameterStrategy.NAMED ) {
session().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class )
.registerRefCursorParameter( statement, getName() );
}
else {
session().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class )
.registerRefCursorParameter( statement, getPosition() );
}
}
}
public int[] getSqlTypes() {
return sqlTypes;
}
@Override
@SuppressWarnings("unchecked")
public T extract(CallableStatement statement) {
if ( mode == ParameterMode.IN ) {
throw new ParameterMisuseException( "IN parameter not valid for output extraction" );
}
else if ( mode == ParameterMode.REF_CURSOR ) {
throw new ParameterMisuseException( "REF_CURSOR parameters should be accessed via results" );
}
try {
if ( ProcedureParameterExtractionAware.class.isInstance( hibernateType ) ) {
return (T) ( (ProcedureParameterExtractionAware) hibernateType ).extract( statement, startIndex, session() );
}
else {
return (T) statement.getObject( startIndex );
}
}
catch (SQLException e) {
throw procedureCall.session().getFactory().getSQLExceptionHelper().convert(
e,
"Unable to extract OUT/INOUT parameter value"
);
}
}
}

View File

@ -0,0 +1,389 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure.internal;
import javax.persistence.ParameterMode;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.jdbc.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.AbstractBasicQueryContractImpl;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.procedure.Call;
import org.hibernate.procedure.NamedParametersNotSupportedException;
import org.hibernate.procedure.ParameterRegistration;
import org.hibernate.procedure.Outputs;
import org.hibernate.type.Type;
/**
* Standard implementation of {@link Call}
*
* @author Steve Ebersole
*/
public class CallImpl extends AbstractBasicQueryContractImpl implements Call {
private final String procedureName;
private final NativeSQLQueryReturn[] queryReturns;
private ParameterStrategy parameterStrategy = ParameterStrategy.UNKNOWN;
private List<ParameterRegistrationImplementor<?>> registeredParameters = new ArrayList<ParameterRegistrationImplementor<?>>();
private Set<String> synchronizedQuerySpaces;
private OutputsImpl outputs;
@SuppressWarnings("unchecked")
public CallImpl(SessionImplementor session, String procedureName) {
this( session, procedureName, (List) null );
}
public CallImpl(SessionImplementor session, String procedureName, List<NativeSQLQueryReturn> queryReturns) {
super( session );
this.procedureName = procedureName;
if ( queryReturns == null || queryReturns.isEmpty() ) {
this.queryReturns = new NativeSQLQueryReturn[0];
}
else {
this.queryReturns = queryReturns.toArray( new NativeSQLQueryReturn[ queryReturns.size() ] );
}
}
public CallImpl(SessionImplementor session, String procedureName, Class... resultClasses) {
this( session, procedureName, collectQueryReturns( resultClasses ) );
}
private static List<NativeSQLQueryReturn> collectQueryReturns(Class[] resultClasses) {
if ( resultClasses == null || resultClasses.length == 0 ) {
return null;
}
List<NativeSQLQueryReturn> queryReturns = new ArrayList<NativeSQLQueryReturn>( resultClasses.length );
int i = 1;
for ( Class resultClass : resultClasses ) {
queryReturns.add( new NativeSQLQueryRootReturn( "alias" + i, resultClass.getName(), LockMode.READ ) );
i++;
}
return queryReturns;
}
public CallImpl(SessionImplementor session, String procedureName, String... resultSetMappings) {
this( session, procedureName, collectQueryReturns( session, resultSetMappings ) );
}
private static List<NativeSQLQueryReturn> collectQueryReturns(SessionImplementor session, String[] resultSetMappings) {
if ( resultSetMappings == null || resultSetMappings.length == 0 ) {
return null;
}
List<NativeSQLQueryReturn> queryReturns = new ArrayList<NativeSQLQueryReturn>( resultSetMappings.length );
for ( String resultSetMapping : resultSetMappings ) {
ResultSetMappingDefinition mapping = session.getFactory().getResultSetMapping( resultSetMapping );
if ( mapping == null ) {
throw new MappingException( "Unknown SqlResultSetMapping [" + resultSetMapping + "]" );
}
queryReturns.addAll( Arrays.asList( mapping.getQueryReturns() ) );
}
return queryReturns;
}
// public CallImpl(
// SessionImplementor session,
// String procedureName,
// List<StoredProcedureParameter> parameters) {
// // this form is intended for named stored procedure calls.
// // todo : introduce a NamedProcedureCallDefinition object to hold all needed info and pass that in here; will help with EM.addNamedQuery as well..
// this( session, procedureName );
// for ( StoredProcedureParameter parameter : parameters ) {
// registerParameter( (StoredProcedureParameterImplementor) parameter );
// }
// }
@Override
public SessionImplementor session() {
// provide access to delegates
return super.session();
}
public ParameterStrategy getParameterStrategy() {
return parameterStrategy;
}
@Override
public String getProcedureName() {
return procedureName;
}
NativeSQLQueryReturn[] getQueryReturns() {
return queryReturns;
}
@Override
@SuppressWarnings("unchecked")
public <T> ParameterRegistration<T> registerParameter(int position, Class<T> type, ParameterMode mode) {
final PositionalParameterRegistration parameterRegistration = new PositionalParameterRegistration( this, position, type, mode );
registerParameter( parameterRegistration );
return parameterRegistration;
}
@Override
@SuppressWarnings("unchecked")
public Call registerParameter0(int position, Class type, ParameterMode mode) {
registerParameter( position, type, mode );
return this;
}
private void registerParameter(ParameterRegistrationImplementor parameter) {
if ( StringHelper.isNotEmpty( parameter.getName() ) ) {
prepareForNamedParameters();
}
else if ( parameter.getPosition() != null ) {
prepareForPositionalParameters();
}
else {
throw new IllegalArgumentException( "Given parameter did not define name or position [" + parameter + "]" );
}
registeredParameters.add( parameter );
}
private void prepareForPositionalParameters() {
if ( parameterStrategy == ParameterStrategy.NAMED ) {
throw new QueryException( "Cannot mix named and positional parameters" );
}
parameterStrategy = ParameterStrategy.POSITIONAL;
}
private void prepareForNamedParameters() {
if ( parameterStrategy == ParameterStrategy.POSITIONAL ) {
throw new QueryException( "Cannot mix named and positional parameters" );
}
if ( parameterStrategy == null ) {
// protect to only do this check once
final ExtractedDatabaseMetaData databaseMetaData = session().getTransactionCoordinator()
.getJdbcCoordinator()
.getLogicalConnection()
.getJdbcServices()
.getExtractedMetaDataSupport();
if ( ! databaseMetaData.supportsNamedParameters() ) {
throw new NamedParametersNotSupportedException(
"Named stored procedure parameters used, but JDBC driver does not support named parameters"
);
}
parameterStrategy = ParameterStrategy.NAMED;
}
}
@Override
public ParameterRegistrationImplementor getParameterRegistration(int position) {
if ( parameterStrategy != ParameterStrategy.POSITIONAL ) {
throw new IllegalArgumentException( "Positions were not used to register parameters with this stored procedure call" );
}
try {
return registeredParameters.get( position );
}
catch ( Exception e ) {
throw new QueryException( "Could not locate parameter registered using that position [" + position + "]" );
}
}
@Override
@SuppressWarnings("unchecked")
public <T> ParameterRegistration<T> registerParameter(String name, Class<T> type, ParameterMode mode) {
final NamedParameterRegistration parameterRegistration = new NamedParameterRegistration( this, name, type, mode );
registerParameter( parameterRegistration );
return parameterRegistration;
}
@Override
@SuppressWarnings("unchecked")
public Call registerParameter0(String name, Class type, ParameterMode mode) {
registerParameter( name, type, mode );
return this;
}
@Override
public ParameterRegistrationImplementor getParameterRegistration(String name) {
if ( parameterStrategy != ParameterStrategy.NAMED ) {
throw new IllegalArgumentException( "Names were not used to register parameters with this stored procedure call" );
}
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
if ( name.equals( parameter.getName() ) ) {
return parameter;
}
}
throw new IllegalArgumentException( "Could not locate parameter registered under that name [" + name + "]" );
}
@Override
@SuppressWarnings("unchecked")
public List<ParameterRegistration> getRegisteredParameters() {
return new ArrayList<ParameterRegistration>( registeredParameters );
}
@Override
public Outputs getOutputs() {
if ( outputs == null ) {
outputs = buildOutputs();
}
return outputs;
}
private OutputsImpl buildOutputs() {
// todo : going to need a very specialized Loader for this.
// or, might be a good time to look at splitting Loader up into:
// 1) building statement objects
// 2) executing statement objects
// 3) processing result sets
// for now assume there are no resultClasses nor mappings defined..
// TOTAL PROOF-OF-CONCEPT!!!!!!
final StringBuilder buffer = new StringBuilder().append( "{call " )
.append( procedureName )
.append( "(" );
String sep = "";
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
for ( int i = 0; i < parameter.getSqlTypes().length; i++ ) {
buffer.append( sep ).append( "?" );
sep = ",";
}
}
buffer.append( ")}" );
try {
final CallableStatement statement = session().getTransactionCoordinator()
.getJdbcCoordinator()
.getLogicalConnection()
.getShareableConnectionProxy()
.prepareCall( buffer.toString() );
// prepare parameters
int i = 1;
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
if ( parameter == null ) {
throw new QueryException( "Registered stored procedure parameters had gaps" );
}
parameter.prepare( statement, i );
i += parameter.getSqlTypes().length;
}
return new OutputsImpl( this, statement );
}
catch (SQLException e) {
throw session().getFactory().getSQLExceptionHelper().convert(
e,
"Error preparing CallableStatement",
getProcedureName()
);
}
}
@Override
public Type[] getReturnTypes() throws HibernateException {
throw new NotYetImplementedException();
}
protected Set<String> synchronizedQuerySpaces() {
if ( synchronizedQuerySpaces == null ) {
synchronizedQuerySpaces = new HashSet<String>();
}
return synchronizedQuerySpaces;
}
@Override
@SuppressWarnings("unchecked")
public Collection<String> getSynchronizedQuerySpaces() {
if ( synchronizedQuerySpaces == null ) {
return Collections.emptySet();
}
else {
return Collections.unmodifiableSet( synchronizedQuerySpaces );
}
}
public Set<String> getSynchronizedQuerySpacesSet() {
return (Set<String>) getSynchronizedQuerySpaces();
}
@Override
public CallImpl addSynchronizedQuerySpace(String querySpace) {
synchronizedQuerySpaces().add( querySpace );
return this;
}
@Override
public CallImpl addSynchronizedEntityName(String entityName) {
addSynchronizedQuerySpaces( session().getFactory().getEntityPersister( entityName ) );
return this;
}
protected void addSynchronizedQuerySpaces(EntityPersister persister) {
synchronizedQuerySpaces().addAll( Arrays.asList( (String[]) persister.getQuerySpaces() ) );
}
@Override
public CallImpl addSynchronizedEntityClass(Class entityClass) {
addSynchronizedQuerySpaces( session().getFactory().getEntityPersister( entityClass.getName() ) );
return this;
}
public QueryParameters buildQueryParametersObject() {
QueryParameters qp = super.buildQueryParametersObject();
// both of these are for documentation purposes, they are actually handled directly...
qp.setAutoDiscoverScalarTypes( true );
qp.setCallable( true );
return qp;
}
public ParameterRegistrationImplementor[] collectRefCursorParameters() {
List<ParameterRegistrationImplementor> refCursorParams = new ArrayList<ParameterRegistrationImplementor>();
for ( ParameterRegistrationImplementor param : registeredParameters ) {
if ( param.getMode() == ParameterMode.REF_CURSOR ) {
refCursorParams.add( param );
}
}
return refCursorParams.toArray( new ParameterRegistrationImplementor[refCursorParams.size()] );
}
}

View File

@ -0,0 +1,39 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure.internal;
import javax.persistence.ParameterMode;
/**
* @author Steve Ebersole
*/
public class NamedParameterRegistration<T> extends AbstractParameterRegistrationImpl<T> {
public NamedParameterRegistration(
CallImpl procedureCall,
String name,
Class<T> type,
ParameterMode mode) {
super( procedureCall, name, type, mode );
}
}

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.internal;
package org.hibernate.procedure.internal;
import java.sql.CallableStatement;
import java.sql.ResultSet;
@ -32,27 +32,24 @@ import java.util.Map;
import java.util.Set;
import org.hibernate.JDBCException;
import org.hibernate.StoredProcedureOutputs;
import org.hibernate.StoredProcedureResultSetReturn;
import org.hibernate.StoredProcedureReturn;
import org.hibernate.StoredProcedureUpdateCountReturn;
import org.hibernate.procedure.Outputs;
import org.hibernate.procedure.ParameterRegistration;
import org.hibernate.procedure.Return;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.StoredProcedureCallImpl.StoredProcedureParameterImplementor;
import org.hibernate.loader.custom.CustomLoader;
import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.loader.custom.Return;
import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor;
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
/**
* @author Steve Ebersole
*/
public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
private final StoredProcedureCallImpl procedureCall;
public class OutputsImpl implements Outputs {
private final CallImpl procedureCall;
private final CallableStatement callableStatement;
private final StoredProcedureParameterImplementor[] refCursorParameters;
private final ParameterRegistrationImplementor[] refCursorParameters;
private final CustomLoaderExtension loader;
private CurrentReturnDescriptor currentReturnDescriptor;
@ -60,7 +57,7 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
private boolean executed = false;
private int refCursorParamIndex = 0;
StoredProcedureOutputsImpl(StoredProcedureCallImpl procedureCall, CallableStatement callableStatement) {
OutputsImpl(CallImpl procedureCall, CallableStatement callableStatement) {
this.procedureCall = procedureCall;
this.callableStatement = callableStatement;
@ -69,14 +66,19 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
this.loader = buildSpecializedCustomLoader( procedureCall );
}
@Override
public <T> T getOutputParameterValue(ParameterRegistration<T> parameterRegistration) {
return ( (ParameterRegistrationImplementor<T>) parameterRegistration ).extract( callableStatement );
}
@Override
public Object getOutputParameterValue(String name) {
return procedureCall.getRegisteredParameter( name ).extract( callableStatement );
return procedureCall.getParameterRegistration( name ).extract( callableStatement );
}
@Override
public Object getOutputParameterValue(int position) {
return procedureCall.getRegisteredParameter( position ).extract( callableStatement );
return procedureCall.getParameterRegistration( position ).extract( callableStatement );
}
@Override
@ -125,7 +127,7 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
}
@Override
public StoredProcedureReturn getNextReturn() {
public Return getNextReturn() {
if ( currentReturnDescriptor == null ) {
if ( executed ) {
throw new IllegalStateException( "Unexpected condition" );
@ -157,7 +159,7 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
this.refCursorParamIndex++;
ResultSet resultSet;
int refCursorParamIndex = copyReturnDescriptor.refCursorParamIndex;
StoredProcedureParameterImplementor refCursorParam = refCursorParameters[refCursorParamIndex];
ParameterRegistrationImplementor refCursorParam = refCursorParameters[refCursorParamIndex];
if ( refCursorParam.getName() != null ) {
resultSet = procedureCall.session().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class )
@ -192,11 +194,11 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
}
}
private static class ResultSetReturn implements StoredProcedureResultSetReturn {
private final StoredProcedureOutputsImpl storedProcedureOutputs;
private static class ResultSetReturn implements org.hibernate.procedure.ResultSetReturn {
private final OutputsImpl storedProcedureOutputs;
private final ResultSet resultSet;
public ResultSetReturn(StoredProcedureOutputsImpl storedProcedureOutputs, ResultSet resultSet) {
public ResultSetReturn(OutputsImpl storedProcedureOutputs, ResultSet resultSet) {
this.storedProcedureOutputs = storedProcedureOutputs;
this.resultSet = resultSet;
}
@ -229,12 +231,12 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
}
}
private class UpdateCountReturn implements StoredProcedureUpdateCountReturn {
private final StoredProcedureOutputsImpl storedProcedureOutputs;
private class UpdateCountReturn implements org.hibernate.procedure.UpdateCountReturn {
private final OutputsImpl procedureOutputs;
private final int updateCount;
public UpdateCountReturn(StoredProcedureOutputsImpl storedProcedureOutputs, int updateCount) {
this.storedProcedureOutputs = storedProcedureOutputs;
public UpdateCountReturn(OutputsImpl procedureOutputs, int updateCount) {
this.procedureOutputs = procedureOutputs;
this.updateCount = updateCount;
}
@ -249,13 +251,13 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
}
}
private static CustomLoaderExtension buildSpecializedCustomLoader(final StoredProcedureCallImpl procedureCall) {
private static CustomLoaderExtension buildSpecializedCustomLoader(final CallImpl procedureCall) {
final SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor(
procedureCall.getQueryReturns(),
procedureCall.session().getFactory()
);
processor.process();
final List<Return> customReturns = processor.generateCustomReturns( false );
final List<org.hibernate.loader.custom.Return> customReturns = processor.generateCustomReturns( false );
CustomQuery customQuery = new CustomQuery() {
@Override
@ -275,7 +277,7 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
}
@Override
public List<Return> getCustomQueryReturns() {
public List<org.hibernate.loader.custom.Return> getCustomQueryReturns() {
return customReturns;
}
};

View File

@ -0,0 +1,57 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure.internal;
import javax.persistence.TemporalType;
import org.hibernate.procedure.ParameterBind;
/**
* Implementation of the {@link ParameterBind} contract.
*
* @author Steve Ebersole
*/
public class ParameterBindImpl<T> implements ParameterBind<T> {
private final T value;
private final TemporalType explicitTemporalType;
public ParameterBindImpl(T value) {
this( value, null );
}
public ParameterBindImpl(T value, TemporalType explicitTemporalType) {
this.value = value;
this.explicitTemporalType = explicitTemporalType;
}
@Override
public T getValue() {
return value;
}
@Override
public TemporalType getExplicitTemporalType() {
return explicitTemporalType;
}
}

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure.internal;
import java.sql.CallableStatement;
import java.sql.SQLException;
import org.hibernate.procedure.ParameterRegistration;
/**
* @author Steve Ebersole
*/
public interface ParameterRegistrationImplementor<T> extends ParameterRegistration<T> {
public void prepare(CallableStatement statement, int i) throws SQLException;
public int[] getSqlTypes();
public T extract(CallableStatement statement);
}

View File

@ -0,0 +1,33 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure.internal;
/**
* The style/strategy of parameter registration used in a particular procedure call definition.
*/
public enum ParameterStrategy {
NAMED,
POSITIONAL,
UNKNOWN
}

View File

@ -0,0 +1,39 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure.internal;
import javax.persistence.ParameterMode;
/**
* @author Steve Ebersole
*/
public class PositionalParameterRegistration<T> extends AbstractParameterRegistrationImpl<T> {
public PositionalParameterRegistration(
CallImpl procedureCall,
Integer position,
Class<T> type,
ParameterMode mode) {
super( procedureCall, position, type, mode );
}
}

View File

@ -0,0 +1,37 @@
package org.hibernate.procedure;
/**
* Defines support for executing database stored procedures and functions and accessing its outputs.
* <p/>
* First a reference to {@link Call} is obtained through one of the overloaded
* {@link org.hibernate.Session#createStoredProcedureCall} methods. The Call reference is then used to "configure"
* the procedure call (set timeouts, etc) and to perform parameter registration. All procedure parameters that the
* application wants to use must be registered. For all IN and INOUT parameters, values can then be bound.
* <p/>
* At this point we are ready to execute the procedure call and start accessing the outputs. This is done by first
* calling the {@link Call#getOutputs()} method. The underlying JDBC call is executed as needed. The pattern to
* access the returns is iterating through the outputs while {@link Outputs#hasMoreReturns()} returns {@code true} and
* calling {@link Outputs#getNextReturn()} during iteration:
* <code>
* Call call = session.createStoredProcedureCall( "some_procedure" );
* ...
* Outputs = call.getOutputs();
* while ( call.hasMoreReturns() ) {
* final Return rtn = call.getNextReturn();
* if ( rtn.isResultSet() ) {
* handleResultSetReturn( (ResultSetReturn) rtn );
* }
* else {
* handleUpdateCountReturn( (UpdateCountReturn) rtn );
* }
* }
* </code>
* <p/>
* Finally output parameters can be accessed using the overloaded {@link Outputs#getOutputParameterValue} methods.
* For portability amongst databases, it is advised to access the output parameters after all returns have been
* processed.
*
* @see org.hibernate.Session#createStoredProcedureCall(String)
* @see org.hibernate.Session#createStoredProcedureCall(String, Class[])
* @see org.hibernate.Session#createStoredProcedureCall(String, String...)
*/

View File

@ -23,24 +23,21 @@
*/
package org.hibernate.test.sql.storedproc;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ParameterMode;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.SQLQuery;
import org.hibernate.JDBCException;
import org.hibernate.Session;
import org.hibernate.StoredProcedureCall;
import org.hibernate.StoredProcedureOutputs;
import org.hibernate.StoredProcedureResultSetReturn;
import org.hibernate.StoredProcedureReturn;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.mapping.AuxiliaryDatabaseObject;
import org.hibernate.procedure.Call;
import org.hibernate.procedure.Outputs;
import org.hibernate.procedure.ResultSetReturn;
import org.hibernate.procedure.Return;
import org.hibernate.dialect.H2Dialect;
import org.junit.Before;
import org.junit.Test;
import org.hibernate.testing.RequiresDialect;
@ -50,67 +47,289 @@ import org.hibernate.testing.junit4.ExtraAssertions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Steve Ebersole
*/
@RequiresDialect( H2Dialect.class )
public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
// this is not working in H2
// @Override
// protected void configure(Configuration configuration) {
// super.configure( configuration );
// configuration.addAuxiliaryDatabaseObject(
// new AuxiliaryDatabaseObject() {
// @Override
// public void addDialectScope(String dialectName) {
// }
//
// @Override
// public boolean appliesToDialect(Dialect dialect) {
// return H2Dialect.class.isInstance( dialect );
// }
//
// @Override
// public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) {
// return "CREATE ALIAS findUser AS $$\n" +
// "import org.h2.tools.SimpleResultSet;\n" +
// "import java.sql.*;\n" +
// "@CODE\n" +
// "ResultSet findUser() {\n" +
// " SimpleResultSet rs = new SimpleResultSet();\n" +
// " rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" +
// " rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" +
// " rs.addRow(1, \"Steve\");\n" +
// " return rs;\n" +
// "}\n" +
// "$$";
// }
//
// @Override
// public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
// return "DROP ALIAS findUser IF EXISTS";
// }
// }
// );
// }
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.addAuxiliaryDatabaseObject(
new AuxiliaryDatabaseObject() {
@Override
public void addDialectScope(String dialectName) {
}
@Override
public boolean appliesToDialect(Dialect dialect) {
return H2Dialect.class.isInstance( dialect );
}
@Override
public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) {
return "CREATE ALIAS findOneUser AS $$\n" +
"import org.h2.tools.SimpleResultSet;\n" +
"import java.sql.*;\n" +
"@CODE\n" +
"ResultSet findOneUser() {\n" +
" SimpleResultSet rs = new SimpleResultSet();\n" +
" rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" +
" rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" +
" rs.addRow(1, \"Steve\");\n" +
" return rs;\n" +
"}\n" +
"$$";
}
@Override
public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
return "DROP ALIAS findUser IF EXISTS";
}
}
);
configuration.addAuxiliaryDatabaseObject(
new AuxiliaryDatabaseObject() {
@Override
public void addDialectScope(String dialectName) {
}
@Override
public boolean appliesToDialect(Dialect dialect) {
return H2Dialect.class.isInstance( dialect );
}
@Override
public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) {
return "CREATE ALIAS findUsers AS $$\n" +
"import org.h2.tools.SimpleResultSet;\n" +
"import java.sql.*;\n" +
"@CODE\n" +
"ResultSet findUsers() {\n" +
" SimpleResultSet rs = new SimpleResultSet();\n" +
" rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" +
" rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" +
" rs.addRow(1, \"Steve\");\n" +
" rs.addRow(2, \"John\");\n" +
" rs.addRow(3, \"Jane\");\n" +
" return rs;\n" +
"}\n" +
"$$";
}
@Override
public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
return "DROP ALIAS findUser IF EXISTS";
}
}
);
configuration.addAuxiliaryDatabaseObject(
new AuxiliaryDatabaseObject() {
@Override
public void addDialectScope(String dialectName) {
}
@Override
public boolean appliesToDialect(Dialect dialect) {
return H2Dialect.class.isInstance( dialect );
}
@Override
public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) {
return "CREATE ALIAS findUserRange AS $$\n" +
"import org.h2.tools.SimpleResultSet;\n" +
"import java.sql.*;\n" +
"@CODE\n" +
"ResultSet findUserRange(int start, int end) {\n" +
" SimpleResultSet rs = new SimpleResultSet();\n" +
" rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" +
" rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" +
" for ( int i = start; i < end; i++ ) {\n" +
" rs.addRow(1, \"User \" + i );\n" +
" }\n" +
" return rs;\n" +
"}\n" +
"$$";
}
@Override
public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
return "DROP ALIAS findUser IF EXISTS";
}
}
);
}
@Test
public void baseTest() {
Session session = openSession();
session.beginTransaction();
StoredProcedureCall query = session.createStoredProcedureCall( "user");
StoredProcedureOutputs outputs = query.getOutputs();
assertTrue( "Checking StoredProcedureOutputs has more returns", outputs.hasMoreReturns() );
StoredProcedureReturn nextReturn = outputs.getNextReturn();
Call query = session.createStoredProcedureCall( "user");
Outputs outputs = query.getOutputs();
assertTrue( "Checking Outputs has more returns", outputs.hasMoreReturns() );
Return nextReturn = outputs.getNextReturn();
assertNotNull( nextReturn );
ExtraAssertions.assertClassAssignability( StoredProcedureResultSetReturn.class, nextReturn.getClass() );
StoredProcedureResultSetReturn resultSetReturn = (StoredProcedureResultSetReturn) nextReturn;
ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() );
ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn;
String name = (String) resultSetReturn.getSingleResult();
assertEquals( "SA", name );
session.getTransaction().commit();
session.close();
}
@Test
public void testGetSingleResultTuple() {
Session session = openSession();
session.beginTransaction();
Call query = session.createStoredProcedureCall( "findOneUser" );
Outputs outputs = query.getOutputs();
assertTrue( "Checking Outputs has more returns", outputs.hasMoreReturns() );
Return nextReturn = outputs.getNextReturn();
assertNotNull( nextReturn );
ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() );
ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn;
Object result = resultSetReturn.getSingleResult();
ExtraAssertions.assertTyping( Object[].class, result );
String name = (String) ( (Object[]) result )[1];
assertEquals( "Steve", name );
session.getTransaction().commit();
session.close();
}
@Test
public void testGetResultListTuple() {
Session session = openSession();
session.beginTransaction();
Call query = session.createStoredProcedureCall( "findUsers" );
Outputs outputs = query.getOutputs();
assertTrue( "Checking Outputs has more returns", outputs.hasMoreReturns() );
Return nextReturn = outputs.getNextReturn();
assertNotNull( nextReturn );
ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() );
ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn;
List results = resultSetReturn.getResultList();
assertEquals( 3, results.size() );
for ( Object result : results ) {
ExtraAssertions.assertTyping( Object[].class, result );
Integer id = (Integer) ( (Object[]) result )[0];
String name = (String) ( (Object[]) result )[1];
if ( id.equals( 1 ) ) {
assertEquals( "Steve", name );
}
else if ( id.equals( 2 ) ) {
assertEquals( "John", name );
}
else if ( id.equals( 3 ) ) {
assertEquals( "Jane", name );
}
else {
fail( "Unexpected id value found [" + id + "]" );
}
}
session.getTransaction().commit();
session.close();
}
@Test
public void testInParametersByName() {
Session session = openSession();
session.beginTransaction();
Call query = session.createStoredProcedureCall( "findUserRange" );
query.registerParameter( "start", Integer.class, ParameterMode.IN ).bindValue( 1 );
query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 );
Outputs outputs = query.getOutputs();
assertTrue( "Checking Outputs has more returns", outputs.hasMoreReturns() );
Return nextReturn = outputs.getNextReturn();
assertNotNull( nextReturn );
ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() );
ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn;
List results = resultSetReturn.getResultList();
assertEquals( 1, results.size() );
Object result = results.get( 0 );
ExtraAssertions.assertTyping( Object[].class, result );
Integer id = (Integer) ( (Object[]) result )[0];
String name = (String) ( (Object[]) result )[1];
assertEquals( 1, (int) id );
assertEquals( "User 1", name );
session.getTransaction().commit();
session.close();
}
@Test
public void testInParametersByPosition() {
Session session = openSession();
session.beginTransaction();
Call query = session.createStoredProcedureCall( "findUserRange" );
query.registerParameter( 1, Integer.class, ParameterMode.IN ).bindValue( 1 );
query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 );
Outputs outputs = query.getOutputs();
assertTrue( "Checking Outputs has more returns", outputs.hasMoreReturns() );
Return nextReturn = outputs.getNextReturn();
assertNotNull( nextReturn );
ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() );
ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn;
List results = resultSetReturn.getResultList();
assertEquals( 1, results.size() );
Object result = results.get( 0 );
ExtraAssertions.assertTyping( Object[].class, result );
Integer id = (Integer) ( (Object[]) result )[0];
String name = (String) ( (Object[]) result )[1];
assertEquals( 1, (int) id );
assertEquals( "User 1", name );
session.getTransaction().commit();
session.close();
}
@Test
public void testInParametersNotSet() {
Session session = openSession();
session.beginTransaction();
// since the procedure does not define defaults for parameters this should result in SQLExceptions on
// execution
{
Call query = session.createStoredProcedureCall( "findUserRange" );
query.registerParameter( 1, Integer.class, ParameterMode.IN );
query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 );
Outputs outputs = query.getOutputs();
try {
outputs.hasMoreReturns();
fail( "Expecting failure due to missing parameter bind" );
}
catch (JDBCException expected) {
}
}
{
Call query = session.createStoredProcedureCall( "findUserRange" );
query.registerParameter( "start", Integer.class, ParameterMode.IN );
query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 );
Outputs outputs = query.getOutputs();
try {
outputs.hasMoreReturns();
fail( "Expecting failure due to missing parameter bind" );
}
catch (JDBCException expected) {
}
}
session.getTransaction().commit();
session.close();
}
}

View File

@ -37,11 +37,11 @@ import java.util.List;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.LockMode;
import org.hibernate.StoredProcedureCall;
import org.hibernate.StoredProcedureOutputs;
import org.hibernate.StoredProcedureResultSetReturn;
import org.hibernate.StoredProcedureReturn;
import org.hibernate.StoredProcedureUpdateCountReturn;
import org.hibernate.procedure.Call;
import org.hibernate.procedure.Outputs;
import org.hibernate.procedure.ResultSetReturn;
import org.hibernate.procedure.Return;
import org.hibernate.procedure.UpdateCountReturn;
import org.hibernate.jpa.spi.BaseQueryImpl;
import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
@ -49,60 +49,61 @@ import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
* @author Steve Ebersole
*/
public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredProcedureQuery {
private final StoredProcedureCall storedProcedureCall;
private StoredProcedureOutputs storedProcedureOutputs;
private final Call procedureCall;
private Outputs procedureOutputs;
public StoredProcedureQueryImpl(StoredProcedureCall storedProcedureCall, HibernateEntityManagerImplementor entityManager) {
public StoredProcedureQueryImpl(Call procedureCall, HibernateEntityManagerImplementor entityManager) {
super( entityManager );
this.storedProcedureCall = storedProcedureCall;
this.procedureCall = procedureCall;
}
@Override
protected boolean applyTimeoutHint(int timeout) {
storedProcedureCall.setTimeout( timeout );
procedureCall.setTimeout( timeout );
return true;
}
@Override
protected boolean applyCacheableHint(boolean isCacheable) {
storedProcedureCall.setCacheable( isCacheable );
procedureCall.setCacheable( isCacheable );
return true;
}
@Override
protected boolean applyCacheRegionHint(String regionName) {
storedProcedureCall.setCacheRegion( regionName );
procedureCall.setCacheRegion( regionName );
return true;
}
@Override
protected boolean applyReadOnlyHint(boolean isReadOnly) {
storedProcedureCall.setReadOnly( isReadOnly );
procedureCall.setReadOnly( isReadOnly );
return true;
}
@Override
protected boolean applyCacheModeHint(CacheMode cacheMode) {
storedProcedureCall.setCacheMode( cacheMode );
procedureCall.setCacheMode( cacheMode );
return true;
}
@Override
protected boolean applyFlushModeHint(FlushMode flushMode) {
storedProcedureCall.setFlushMode( flushMode );
procedureCall.setFlushMode( flushMode );
return true;
}
@Override
@SuppressWarnings("unchecked")
public StoredProcedureQuery registerStoredProcedureParameter(int position, Class type, ParameterMode mode) {
storedProcedureCall.registerStoredProcedureParameter( position, type, mode );
procedureCall.registerParameter( position, type, mode );
return this;
}
@Override
@SuppressWarnings("unchecked")
public StoredProcedureQuery registerStoredProcedureParameter(String parameterName, Class type, ParameterMode mode) {
storedProcedureCall.registerStoredProcedureParameter( parameterName, type, mode );
procedureCall.registerParameter( parameterName, type, mode );
return this;
}
@ -171,11 +172,11 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
// outputs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private StoredProcedureOutputs outputs() {
if ( storedProcedureOutputs == null ) {
storedProcedureOutputs = storedProcedureCall.getOutputs();
private Outputs outputs() {
if ( procedureOutputs == null ) {
procedureOutputs = procedureCall.getOutputs();
}
return storedProcedureOutputs;
return procedureOutputs;
}
@Override
@ -205,29 +206,29 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
@Override
public int getUpdateCount() {
final StoredProcedureReturn nextReturn = outputs().getNextReturn();
final Return nextReturn = outputs().getNextReturn();
if ( nextReturn.isResultSet() ) {
return -1;
}
return ( (StoredProcedureUpdateCountReturn) nextReturn ).getUpdateCount();
return ( (UpdateCountReturn) nextReturn ).getUpdateCount();
}
@Override
public List getResultList() {
final StoredProcedureReturn nextReturn = outputs().getNextReturn();
final Return nextReturn = outputs().getNextReturn();
if ( ! nextReturn.isResultSet() ) {
return null; // todo : what should be thrown/returned here?
}
return ( (StoredProcedureResultSetReturn) nextReturn ).getResultList();
return ( (ResultSetReturn) nextReturn ).getResultList();
}
@Override
public Object getSingleResult() {
final StoredProcedureReturn nextReturn = outputs().getNextReturn();
final Return nextReturn = outputs().getNextReturn();
if ( ! nextReturn.isResultSet() ) {
return null; // todo : what should be thrown/returned here?
}
return ( (StoredProcedureResultSetReturn) nextReturn ).getSingleResult();
return ( (ResultSetReturn) nextReturn ).getSingleResult();
}
@Override

View File

@ -83,7 +83,7 @@ import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.StoredProcedureCall;
import org.hibernate.procedure.Call;
import org.hibernate.TransientObjectException;
import org.hibernate.TypeMismatchException;
import org.hibernate.UnresolvableObjectException;
@ -813,7 +813,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
try {
StoredProcedureCall call = getSession().createStoredProcedureCall( procedureName );
Call call = getSession().createStoredProcedureCall( procedureName );
return new StoredProcedureQueryImpl( call, this );
}
catch ( HibernateException he ) {
@ -824,7 +824,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) {
try {
StoredProcedureCall call = getSession().createStoredProcedureCall( procedureName, resultClasses );
Call call = getSession().createStoredProcedureCall( procedureName, resultClasses );
return new StoredProcedureQueryImpl( call, this );
}
catch ( HibernateException he ) {