HHH-7914 - Improve new stored procedure call support
This commit is contained in:
parent
0713cea180
commit
5de1677ce7
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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> {
|
||||
}
|
|
@ -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> {
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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> {
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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.
|
||||
*
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()] );
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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...)
|
||||
*/
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
Loading…
Reference in New Issue