HHH-7387 - Integrate Draft 6 of the JPA 2.1 spec : stored procedure queries

This commit is contained in:
Steve Ebersole 2012-06-28 20:14:12 -05:00
parent 4174c14675
commit 153eb4a913
48 changed files with 3216 additions and 404 deletions

View File

@ -0,0 +1,215 @@
/*
* 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 org.hibernate.type.Type;
/**
* Defines the aspects of query definition that apply to all forms of querying.
*
* @author Steve Ebersole
*/
public interface BasicQueryContract {
/**
* Obtain the FlushMode in effect for this query. By default, the query inherits the FlushMode of the Session
* from which is originates.
*
* @return The query FlushMode.
*
* @see Session#getFlushMode()
* @see FlushMode
*/
public FlushMode getFlushMode();
/**
* (Re)set the current FlushMode in effect for this query.
*
* @param flushMode The new FlushMode to use.
*
* @see #getFlushMode()
*/
public BasicQueryContract setFlushMode(FlushMode flushMode);
/**
* Obtain the CacheMode in effect for this query. By default, the query inherits the CacheMode of the Session
* from which is originates.
*
* NOTE: The CacheMode here only effects reading/writing of the query cache, not the
* entity/collection caches.
*
* @return The query CacheMode.
*
* @see Session#getCacheMode()
* @see CacheMode
*/
public CacheMode getCacheMode();
/**
* (Re)set the current CacheMode in effect for this query.
*
* @param cacheMode The new CacheMode to use.
*
* @see #getCacheMode()
*/
public BasicQueryContract setCacheMode(CacheMode cacheMode);
/**
* Are the results of this query eligible for second level query caching? This is different that second level
* caching of any returned entities and collections.
*
* NOTE: the query being "eligible" for caching does not necessarily mean its results will be cached. Second level
* query caching still has to be enabled on the {@link SessionFactory} for this to happen. Usually that is
* controlled by the {@code hibernate.cache.use_query_cache} configuration setting.
*
* @return {@code true} if the query results are eligible for caching, {@code false} otherwise.
*
* @see org.hibernate.cfg.AvailableSettings#USE_QUERY_CACHE
*/
public boolean isCacheable();
/**
* Enable/disable second level query (result) caching for this query.
*
* @param cacheable Should the query results be cacheable?
*
* @see #isCacheable
*/
public BasicQueryContract setCacheable(boolean cacheable);
/**
* Obtain the name of the second level query cache region in which query results will be stored (if they are
* cached, see the discussion on {@link #isCacheable()} for more information). {@code null} indicates that the
* default region should be used.
*
* @return The specified cache region name into which query results should be placed; {@code null} indicates
* the default region.
*/
public String getCacheRegion();
/**
* Set the name of the cache region where query results should be cached (if cached at all).
*
* @param cacheRegion the name of a query cache region, or {@code null} to indicate that the default region
* should be used.
*
* @see #getCacheRegion()
*/
public BasicQueryContract setCacheRegion(String cacheRegion);
/**
* Obtain the query timeout <b>in seconds</b>. This value is eventually passed along to the JDBC query via
* {@link java.sql.Statement#setQueryTimeout(int)}. Zero indicates no timeout.
*
* @return The timeout <b>in seconds</b>
*
* @see java.sql.Statement#getQueryTimeout()
* @see java.sql.Statement#setQueryTimeout(int)
*/
public Integer getTimeout();
/**
* Set the query timeout <b>in seconds</b>.
*
* NOTE it is important to understand that any value set here is eventually passed directly through to the JDBC
* Statement which expressly disallows negative values. So negative values should be avoided as a general rule.
*
* @param timeout the timeout <b>in seconds</b>
*
* @see #getTimeout()
*/
public BasicQueryContract setTimeout(int timeout);
/**
* Obtain the JDBC fetch size hint in effect for this query. This value is eventually passed along to the JDBC
* query via {@link java.sql.Statement#setFetchSize(int)}. As defined b y JDBC, this value is a hint to the
* driver to indicate how many rows to fetch from the database when more rows are needed.
*
* NOTE : JDBC expressly defines this value as a hint. It may or may not have any effect on the actual
* query execution and ResultSet processing depending on the driver.
*
* @return The timeout <b>in seconds</b>
*
* @see java.sql.Statement#getFetchSize()
* @see java.sql.Statement#setFetchSize(int)
*/
public Integer getFetchSize();
/**
* Sets a JDBC fetch size hint for the query.
*
* @param fetchSize the fetch size hint
*
* @see #getFetchSize()
*/
public BasicQueryContract setFetchSize(int fetchSize);
/**
* Should entities and proxies loaded by this Query be put in read-only mode? If the
* read-only/modifiable setting was not initialized, then the default
* read-only/modifiable setting for the persistence context is returned instead.
* @see Query#setReadOnly(boolean)
* @see org.hibernate.engine.spi.PersistenceContext#isDefaultReadOnly()
*
* The read-only/modifiable setting has no impact on entities/proxies returned by the
* query that existed in the session before the query was executed.
*
* @return true, entities and proxies loaded by the query will be put in read-only mode
* false, entities and proxies loaded by the query will be put in modifiable mode
*/
public boolean isReadOnly();
/**
* Set the read-only/modifiable mode for entities and proxies
* loaded by this Query. This setting overrides the default setting
* for the persistence context.
* @see org.hibernate.engine.spi.PersistenceContext#isDefaultReadOnly()
*
* To set the default read-only/modifiable setting used for
* entities and proxies that are loaded into the session:
* @see org.hibernate.engine.spi.PersistenceContext#setDefaultReadOnly(boolean)
* @see org.hibernate.Session#setDefaultReadOnly(boolean)
*
* Read-only entities are not dirty-checked and snapshots of persistent
* state are not maintained. Read-only entities can be modified, but
* changes are not persisted.
*
* When a proxy is initialized, the loaded entity will have the same
* read-only/modifiable setting as the uninitialized
* proxy has, regardless of the session's current setting.
*
* The read-only/modifiable setting has no impact on entities/proxies
* returned by the query that existed in the session before the query was executed.
*
* @param readOnly true, entities and proxies loaded by the query will be put in read-only mode
* false, entities and proxies loaded by the query will be put in modifiable mode
*/
public BasicQueryContract setReadOnly(boolean readOnly);
/**
* Return the Hibernate types of the query results.
*
* @return an array of types
*/
public Type[] getReturnTypes() throws HibernateException;
}

View File

@ -77,7 +77,7 @@ import org.hibernate.type.Type;
*
* @author Gavin King
*/
public interface Query {
public interface Query extends BasicQueryContract {
/**
* Get the query string.
*
@ -121,139 +121,27 @@ public interface Query {
*/
public Query setFirstResult(int firstResult);
/**
* Obtain the FlushMode in effect for this query. By default, the query inherits the FlushMode of the Session
* from which is originates.
*
* @return The query FlushMode.
*
* @see Session#getFlushMode()
* @see FlushMode
*/
public FlushMode getFlushMode();
/**
* (Re)set the current FlushMode in effect for this query.
*
* @param flushMode The new FlushMode to use.
*
* @see #getFlushMode()
*/
@Override
public Query setFlushMode(FlushMode flushMode);
/**
* Obtain the CacheMode in effect for this query. By default, the query inherits the CacheMode of the Session
* from which is originates.
*
* NOTE: The CacheMode here only effects reading/writing of the query cache, not the
* entity/collection caches.
*
* @return The query CacheMode.
*
* @see Session#getCacheMode()
* @see CacheMode
*/
public CacheMode getCacheMode();
/**
* (Re)set the current CacheMode in effect for this query.
*
* @param cacheMode The new CacheMode to use.
*
* @see #getCacheMode()
*/
@Override
public Query setCacheMode(CacheMode cacheMode);
/**
* Are the results of this query eligible for second level query caching? This is different that second level
* caching of any returned entities and collections.
*
* NOTE: the query being "eligible" for caching does not necessarily mean its results will be cached. Second level
* query caching still has to be enabled on the {@link SessionFactory} for this to happen. Usually that is
* controlled by the {@code hibernate.cache.use_query_cache} configuration setting.
*
* @return {@code true} if the query results are eligible for caching, {@code false} otherwise.
*
* @see org.hibernate.cfg.AvailableSettings#USE_QUERY_CACHE
*/
public boolean isCacheable();
/**
* Enable/disable second level query (result) caching for this query.
*
* @param cacheable Should the query results be cacheable?
*
* @see #isCacheable
*/
@Override
public Query setCacheable(boolean cacheable);
/**
* Obtain the name of the second level query cache region in which query results will be stored (if they are
* cached, see the discussion on {@link #isCacheable()} for more information). {@code null} indicates that the
* default region should be used.
*
* @return The specified cache region name into which query results should be placed; {@code null} indicates
* the default region.
*/
public String getCacheRegion();
/**
* Set the name of the cache region where query results should be cached (if cached at all).
*
* @param cacheRegion the name of a query cache region, or {@code null} to indicate that the default region
* should be used.
*
* @see #getCacheRegion()
*/
@Override
public Query setCacheRegion(String cacheRegion);
/**
* Obtain the query timeout <b>in seconds</b>. This value is eventually passed along to the JDBC query via
* {@link java.sql.Statement#setQueryTimeout(int)}. Zero indicates no timeout.
*
* @return The timeout <b>in seconds</b>
*
* @see java.sql.Statement#getQueryTimeout()
* @see java.sql.Statement#setQueryTimeout(int)
*/
public Integer getTimeout();
/**
* Set the query timeout <b>in seconds</b>.
*
* NOTE it is important to understand that any value set here is eventually passed directly through to the JDBC
* Statement which expressly disallows negative values. So negative values should be avoided as a general rule.
*
* @param timeout the timeout <b>in seconds</b>
*
* @see #getTimeout()
*/
@Override
public Query setTimeout(int timeout);
/**
* Obtain the JDBC fetch size hint in effect for this query. This value is eventually passed along to the JDBC
* query via {@link java.sql.Statement#setFetchSize(int)}. As defined b y JDBC, this value is a hint to the
* driver to indicate how many rows to fetch from the database when more rows are needed.
*
* NOTE : JDBC expressly defines this value as a hint. It may or may not have any effect on the actual
* query execution and ResultSet processing depending on the driver.
*
* @return The timeout <b>in seconds</b>
*
* @see java.sql.Statement#getFetchSize()
* @see java.sql.Statement#setFetchSize(int)
*/
public Integer getFetchSize();
/**
* Sets a JDBC fetch size hint for the query.
*
* @param fetchSize the fetch size hint
*
* @see #getFetchSize()
*/
@Override
public Query setFetchSize(int fetchSize);
@Override
public Query setReadOnly(boolean readOnly);
/**
* Obtains the LockOptions in effect for this query.
*
@ -310,54 +198,6 @@ public interface Query {
*/
public Query setComment(String comment);
/**
* Should entities and proxies loaded by this Query be put in read-only mode? If the
* read-only/modifiable setting was not initialized, then the default
* read-only/modifiable setting for the persistence context is returned instead.
* @see Query#setReadOnly(boolean)
* @see org.hibernate.engine.spi.PersistenceContext#isDefaultReadOnly()
*
* The read-only/modifiable setting has no impact on entities/proxies returned by the
* query that existed in the session before the query was executed.
*
* @return true, entities and proxies loaded by the query will be put in read-only mode
* false, entities and proxies loaded by the query will be put in modifiable mode
*/
public boolean isReadOnly();
/**
* Set the read-only/modifiable mode for entities and proxies
* loaded by this Query. This setting overrides the default setting
* for the persistence context.
* @see org.hibernate.engine.spi.PersistenceContext#isDefaultReadOnly()
*
* To set the default read-only/modifiable setting used for
* entities and proxies that are loaded into the session:
* @see org.hibernate.engine.spi.PersistenceContext#setDefaultReadOnly(boolean)
* @see org.hibernate.Session#setDefaultReadOnly(boolean)
*
* Read-only entities are not dirty-checked and snapshots of persistent
* state are not maintained. Read-only entities can be modified, but
* changes are not persisted.
*
* When a proxy is initialized, the loaded entity will have the same
* read-only/modifiable setting as the uninitialized
* proxy has, regardless of the session's current setting.
*
* The read-only/modifiable setting has no impact on entities/proxies
* returned by the query that existed in the session before the query was executed.
*
* @param readOnly true, entities and proxies loaded by the query will be put in read-only mode
* false, entities and proxies loaded by the query will be put in modifiable mode
*/
public Query setReadOnly(boolean readOnly);
/**
* Return the Hibernate types of the query result set.
* @return an array of types
*/
public Type[] getReturnTypes() throws HibernateException;
/**
* Return the HQL select clause aliases (if any)
* @return an array of aliases as strings

View File

@ -22,7 +22,6 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate;
import java.util.Collection;
import java.util.List;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
@ -48,54 +47,15 @@ import org.hibernate.type.Type;
* @author Gavin King
* @author Steve Ebersole
*/
public interface SQLQuery extends Query {
/**
* Obtain the list of query spaces (table names) the query is synchronized on. These spaces affect the process
* of auto-flushing by determining which entities will be processed by auto-flush based on the table to
* which those entities are mapped and which are determined to have pending state changes.
*
* @return The list of query spaces upon which the query is synchronized.
*/
public Collection<String> getSynchronizedQuerySpaces();
public interface SQLQuery extends Query, SynchronizeableQuery {
@Override
SQLQuery addSynchronizedQuerySpace(String querySpace);
/**
* Adds a query space (table name) for (a) auto-flush checking and (b) query result cache invalidation checking
*
* @param querySpace The query space to be auto-flushed for this query.
*
* @return this, for method chaining
*
* @see #getSynchronizedQuerySpaces()
*/
public SQLQuery addSynchronizedQuerySpace(String querySpace);
@Override
SQLQuery addSynchronizedEntityName(String entityName) throws MappingException;
/**
* Adds an entity name for (a) auto-flush checking and (b) query result cache invalidation checking. Same as
* {@link #addSynchronizedQuerySpace} for all tables associated with the given entity.
*
* @param entityName The name of the entity upon whose defined query spaces we should additionally synchronize.
*
* @return this, for method chaining
*
* @throws MappingException Indicates the given name could not be resolved as an entity
*
* @see #getSynchronizedQuerySpaces()
*/
public SQLQuery addSynchronizedEntityName(String entityName) throws MappingException;
/**
* Adds an entity for (a) auto-flush checking and (b) query result cache invalidation checking. Same as
* {@link #addSynchronizedQuerySpace} for all tables associated with the given entity.
*
* @param entityClass The class of the entity upon whose defined query spaces we should additionally synchronize.
*
* @return this, for method chaining
*
* @throws MappingException Indicates the given class could not be resolved as an entity
*
* @see #getSynchronizedQuerySpaces()
*/
public SQLQuery addSynchronizedEntityClass(Class entityClass) throws MappingException;
@Override
SQLQuery addSynchronizedEntityClass(Class entityClass) throws MappingException;
/**
* Use a predefined named result-set mapping. This might be defined by a {@code <result-set/>} element in a

View File

@ -84,6 +84,35 @@ public interface SharedSessionContract extends Serializable {
*/
public SQLQuery createSQLQuery(String queryString);
/**
* Creates a call to a stored procedure.
*
* @param procedureName The name of the procedure.
*
* @return The representation of the procedure call.
*/
public StoredProcedureCall createStoredProcedureCall(String procedureName);
/**
* Creates a call to a stored procedure with specific result set entity mappings
*
* @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);
/**
* Creates a call to a stored procedure with specific result set entity mappings
*
* @param procedureName The name of the procedure.
* @param resultSetMappings The explicit result set mapping(s) to use for mapping the results
*
* @return The representation of the procedure call.
*/
public StoredProcedureCall createStoredProcedureCall(String procedureName, String... resultSetMappings);
/**
* Create {@link Criteria} instance for the given class (entity or subclasses/implementors)
*
@ -122,4 +151,5 @@ public interface SharedSessionContract extends Serializable {
* @return The criteria instance for manipulation and execution
*/
public Criteria createCriteria(String entityName, String alias);
}

View File

@ -0,0 +1,197 @@
/*
* 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.type.Type;
/**
* @author Steve Ebersole
*/
public interface StoredProcedureCall extends BasicQueryContract, SynchronizeableQuery {
@Override
StoredProcedureCall addSynchronizedQuerySpace(String querySpace);
@Override
StoredProcedureCall addSynchronizedEntityName(String entityName) throws MappingException;
@Override
StoredProcedureCall addSynchronizedEntityClass(Class entityClass) throws MappingException;
/**
* Get the name of the stored procedure to be called.
*
* @return The procedure name.
*/
public String getProcedureName();
/**
* Register a positional parameter.
* All positional parameters must be registered.
*
* @param position parameter position
* @param type type of the parameter
* @param mode parameter mode
*
* @return the same query instance
*/
StoredProcedureCall registerStoredProcedureParameter(
int position,
Class type,
ParameterMode mode);
/**
* Register a named parameter.
* When using parameter names, all parameters must be registered
* in the order in which they occur in the parameter list of the
* stored procedure.
*
* @param parameterName name of the parameter as registered or
* <p/>
* specified in metadata
* @param type type of the parameter
* @param mode parameter mode
*
* @return the same query instance
*/
StoredProcedureCall registerStoredProcedureParameter(
String parameterName,
Class type,
ParameterMode mode);
/**
* Retrieve all registered parameters.
*
* @return The (immutable) list of all registered parameters.
*/
public List<StoredProcedureParameter> getRegisteredParameters();
/**
* Retrieve parameter registered by name.
*
* @param name The name under which the parameter of interest was registered.
*
* @return The registered parameter.
*/
public StoredProcedureParameter getRegisteredParameter(String name);
public StoredProcedureParameter getRegisteredParameter(int position);
public StoredProcedureOutputs getOutputs();
/**
* Describes a parameter registered with the stored procedure. Parameters can be either named or positional
* as the registration mechanism. Named and positional should not be mixed.
*/
public static interface StoredProcedureParameter<T> {
/**
* The name under which this parameter was registered. Can be {@code null} which should indicate that
* positional registration was used (and therefore {@link #getPosition()} should return non-null.
*
* @return The name;
*/
public String getName();
/**
* The position at which this parameter was registered. Can be {@code null} which should indicate that
* named registration was used (and therefore {@link #getName()} should return non-null.
*
* @return The name;
*/
public Integer getPosition();
/**
* Obtain the Java type of parameter. This is used to guess the Hibernate type (unless {@link #setHibernateType}
* is called explicitly).
*
* @return The parameter Java type.
*/
public Class<T> getType();
/**
* Retrieves the parameter "mode" which describes how the parameter is defined in the actual database procedure
* definition (is it an INPUT parameter? An OUTPUT parameter? etc).
*
* @return The parameter mode.
*/
public ParameterMode getMode();
/**
* Set the Hibernate mapping type for this parameter.
*
* @param type The Hibernate mapping type.
*/
public void setHibernateType(Type type);
/**
* Retrieve the binding associated with this parameter. The binding is only relevant for INPUT parameters. Can
* return {@code null} if nothing has been bound yet. To bind a value to the parameter use one of the
* {@link #bindValue} methods.
*
* @return The parameter binding
*/
public StoredProcedureParameterBind getParameterBind();
/**
* Bind a value to the parameter. How this value is bound to the underlying JDBC CallableStatement is
* totally dependent on the Hibernate type.
*
* @param value The value to bind.
*/
public void bindValue(T value);
/**
* Bind a value to the parameter, using just a specified portion of the DATE/TIME value. It is illegal to call
* this form if the parameter is not DATE/TIME type. The Hibernate type is circumvented in this case and
* an appropriate "precision" Type is used instead.
*
* @param value The value to bind
* @param explicitTemporalType An explicitly supplied TemporalType.
*/
public void bindValue(T value, TemporalType explicitTemporalType);
}
/**
* Describes an input value binding for any IN/INOUT parameters.
*/
public static interface StoredProcedureParameterBind<T> {
/**
* Retrieves the bound value.
*
* @return The bound value.
*/
public T getValue();
/**
* If {@code <T>} represents a DATE/TIME type value, JPA usually allows specifying the particular parts of
* the DATE/TIME value to be bound. This value represents the particular part the user requested to be bound.
*
* @return The explicitly supplied TemporalType.
*/
public TemporalType getExplicitTemporalType();
}
}

View File

@ -0,0 +1,69 @@
/*
* 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;
/**
* Represents all the outputs of a call to a database stored procedure (or function) through the JDBC
* {@link java.sql.CallableStatement} interface.
*
* @author Steve Ebersole
*/
public interface StoredProcedureOutputs {
/**
* Retrieve the value of an OUTPUT parameter by the name under which the parameter was registered.
*
* @param name The name under which the parameter was registered.
*
* @return The output value.
*
* @see StoredProcedureCall#registerStoredProcedureParameter(String, Class, javax.persistence.ParameterMode)
*/
public Object getOutputParameterValue(String name);
/**
* Retrieve the value of an OUTPUT parameter by the name position under which the parameter was registered.
*
* @param position The position at which the parameter was registered.
*
* @return The output value.
*
* @see StoredProcedureCall#registerStoredProcedureParameter(int, Class, javax.persistence.ParameterMode)
*/
public Object getOutputParameterValue(int position);
/**
* Are there any more returns associated with this set of outputs?
*
* @return {@code true} means there are more results available via {@link #getNextReturn()}; {@code false}
* indicates that calling {@link #getNextReturn()} will certainly result in an exception.
*/
public boolean hasMoreReturns();
/**
* Retrieve the next return.
*
* @return The next return.
*/
public StoredProcedureReturn getNextReturn();
}

View File

@ -0,0 +1,48 @@
/*
* 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 java.util.List;
/**
* Models a stored procedure result that is a result set.
*
* @author Steve Ebersole
*/
public interface StoredProcedureResultSetReturn extends StoredProcedureReturn {
/**
* Consume the underlying {@link java.sql.ResultSet} and return the resulting List.
*
* @return The consumed ResultSet values.
*/
public List getResultList();
/**
* Consume the underlying {@link java.sql.ResultSet} with the expectation that there is just a single level of
* root returns.
*
* @return The single result.
*/
public Object getSingleResult();
}

View File

@ -0,0 +1,31 @@
/*
* 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;
/**
* @author Steve Ebersole
*/
public interface StoredProcedureReturn {
public boolean isResultSet();
}

View File

@ -0,0 +1,31 @@
/*
* 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;
/**
* @author Steve Ebersole
*/
public interface StoredProcedureUpdateCountReturn extends StoredProcedureReturn {
public int getUpdateCount();
}

View File

@ -0,0 +1,79 @@
/*
* 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 java.util.Collection;
/**
* @author Steve Ebersole
*/
public interface SynchronizeableQuery {
/**
* Obtain the list of query spaces (table names) the query is synchronized on. These spaces affect the process
* of auto-flushing by determining which entities will be processed by auto-flush based on the table to
* which those entities are mapped and which are determined to have pending state changes.
*
* @return The list of query spaces upon which the query is synchronized.
*/
public Collection<String> getSynchronizedQuerySpaces();
/**
* Adds a query space (table name) for (a) auto-flush checking and (b) query result cache invalidation checking
*
* @param querySpace The query space to be auto-flushed for this query.
*
* @return this, for method chaining
*
* @see #getSynchronizedQuerySpaces()
*/
public SynchronizeableQuery addSynchronizedQuerySpace(String querySpace);
/**
* Adds an entity name for (a) auto-flush checking and (b) query result cache invalidation checking. Same as
* {@link #addSynchronizedQuerySpace} for all tables associated with the given entity.
*
* @param entityName The name of the entity upon whose defined query spaces we should additionally synchronize.
*
* @return this, for method chaining
*
* @throws MappingException Indicates the given name could not be resolved as an entity
*
* @see #getSynchronizedQuerySpaces()
*/
public SynchronizeableQuery addSynchronizedEntityName(String entityName) throws MappingException;
/**
* Adds an entity for (a) auto-flush checking and (b) query result cache invalidation checking. Same as
* {@link #addSynchronizedQuerySpace} for all tables associated with the given entity.
*
* @param entityClass The class of the entity upon whose defined query spaces we should additionally synchronize.
*
* @return this, for method chaining
*
* @throws MappingException Indicates the given class could not be resolved as an entity
*
* @see #getSynchronizedQuerySpaces()
*/
public SynchronizeableQuery addSynchronizedEntityClass(Class entityClass) throws MappingException;
}

View File

@ -0,0 +1,146 @@
/*
* 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 java.io.Serializable;
import java.util.Map;
import org.hibernate.BasicQueryContract;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionImplementor;
/**
* @author Steve Ebersole
*/
public abstract class AbstractBasicQueryContractImpl implements BasicQueryContract {
private final SessionImplementor session;
private FlushMode flushMode;
private CacheMode cacheMode;
private boolean cacheable;
private String cacheRegion;
private boolean readOnly;
private RowSelection selection = new RowSelection();
protected AbstractBasicQueryContractImpl(SessionImplementor session) {
this.session = session;
this.readOnly = session.getPersistenceContext().isDefaultReadOnly();
}
protected SessionImplementor session() {
return session;
}
@Override
public FlushMode getFlushMode() {
return flushMode;
}
@Override
public BasicQueryContract setFlushMode(FlushMode flushMode) {
this.flushMode = flushMode;
return this;
}
@Override
public CacheMode getCacheMode() {
return cacheMode;
}
@Override
public BasicQueryContract setCacheMode(CacheMode cacheMode) {
this.cacheMode = cacheMode;
return this;
}
@Override
public boolean isCacheable() {
return cacheable;
}
@Override
public BasicQueryContract setCacheable(boolean cacheable) {
this.cacheable = cacheable;
return this;
}
@Override
public String getCacheRegion() {
return cacheRegion;
}
@Override
public BasicQueryContract setCacheRegion(String cacheRegion) {
if ( cacheRegion != null ) {
this.cacheRegion = cacheRegion.trim();
}
return this;
}
@Override
public boolean isReadOnly() {
return readOnly;
}
@Override
public BasicQueryContract setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
return this;
}
@Override
public Integer getTimeout() {
return selection.getTimeout();
}
@Override
public BasicQueryContract setTimeout(int timeout) {
selection.setTimeout( timeout );
return this;
}
@Override
public Integer getFetchSize() {
return selection.getFetchSize();
}
@Override
public BasicQueryContract setFetchSize(int fetchSize) {
selection.setFetchSize( fetchSize );
return this;
}
public QueryParameters buildQueryParametersObject() {
QueryParameters qp = new QueryParameters();
qp.setRowSelection( selection );
qp.setCacheable( cacheable );
qp.setCacheRegion( cacheRegion );
qp.setReadOnly( readOnly );
return qp;
}
}

View File

@ -36,6 +36,7 @@ import org.hibernate.SQLQuery;
import org.hibernate.ScrollableResults;
import org.hibernate.SessionException;
import org.hibernate.SharedSessionContract;
import org.hibernate.StoredProcedureCall;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
@ -235,6 +236,33 @@ public abstract class AbstractSessionImpl implements Serializable, SharedSession
return query;
}
@Override
@SuppressWarnings("UnnecessaryLocalVariable")
public StoredProcedureCall createStoredProcedureCall(String procedureName) {
errorIfClosed();
final StoredProcedureCall call = new StoredProcedureCallImpl( this, procedureName );
// call.setComment( "Dynamic stored procedure call" );
return call;
}
@Override
@SuppressWarnings("UnnecessaryLocalVariable")
public StoredProcedureCall createStoredProcedureCall(String procedureName, Class... resultClasses) {
errorIfClosed();
final StoredProcedureCall call = new StoredProcedureCallImpl( this, procedureName, resultClasses );
// call.setComment( "Dynamic stored procedure call" );
return call;
}
@Override
@SuppressWarnings("UnnecessaryLocalVariable")
public StoredProcedureCall createStoredProcedureCall(String procedureName, String... resultSetMappings) {
errorIfClosed();
final StoredProcedureCall call = new StoredProcedureCallImpl( this, procedureName, resultSetMappings );
// call.setComment( "Dynamic stored procedure call" );
return call;
}
protected HQLQueryPlan getHQLQueryPlan(String query, boolean shallow) throws HibernateException {
return factory.getQueryPlanCache().getHQLQueryPlan( query, shallow, getEnabledFilters() );
}

View File

@ -31,7 +31,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
@ -101,44 +100,15 @@ public class SQLQueryImpl extends AbstractQueryImpl implements SQLQuery {
this.callable = queryDef.isCallable();
}
SQLQueryImpl(
final String sql,
final String returnAliases[],
final Class returnClasses[],
final LockMode[] lockModes,
final SessionImplementor session,
final Collection<String> querySpaces,
final FlushMode flushMode,
ParameterMetadata parameterMetadata) {
// TODO : this constructor form is *only* used from constructor directly below us; can it go away?
super( sql, flushMode, session, parameterMetadata );
queryReturns = new ArrayList<NativeSQLQueryReturn>( returnAliases.length );
for ( int i=0; i<returnAliases.length; i++ ) {
NativeSQLQueryRootReturn ret = new NativeSQLQueryRootReturn(
returnAliases[i],
returnClasses[i].getName(),
lockModes==null ? LockMode.NONE : lockModes[i]
);
queryReturns.add(ret);
}
this.querySpaces = querySpaces;
this.callable = false;
}
SQLQueryImpl(
final String sql,
final String returnAliases[],
final Class returnClasses[],
final SessionImplementor session,
ParameterMetadata parameterMetadata) {
this( sql, returnAliases, returnClasses, null, session, null, null, parameterMetadata );
}
SQLQueryImpl(String sql, SessionImplementor session, ParameterMetadata parameterMetadata) {
this( sql, false, session, parameterMetadata );
}
SQLQueryImpl(String sql, boolean callable, SessionImplementor session, ParameterMetadata parameterMetadata) {
super( sql, null, session, parameterMetadata );
queryReturns = new ArrayList<NativeSQLQueryReturn>();
querySpaces = null;
callable = false;
this.queryReturns = new ArrayList<NativeSQLQueryReturn>();
this.querySpaces = null;
this.callable = callable;
}
@Override

View File

@ -73,6 +73,7 @@ import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.SessionException;
import org.hibernate.StoredProcedureCall;
import org.hibernate.engine.spi.SessionOwner;
import org.hibernate.SharedSessionBuilder;
import org.hibernate.SimpleNaturalIdLoadAccess;
@ -1742,6 +1743,27 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
return super.createSQLQuery( sql );
}
@Override
public StoredProcedureCall createStoredProcedureCall(String procedureName) {
errorIfClosed();
checkTransactionSynchStatus();
return super.createStoredProcedureCall( procedureName );
}
@Override
public StoredProcedureCall createStoredProcedureCall(String procedureName, String... resultSetMappings) {
errorIfClosed();
checkTransactionSynchStatus();
return super.createStoredProcedureCall( procedureName, resultSetMappings );
}
@Override
public StoredProcedureCall createStoredProcedureCall(String procedureName, Class... resultClasses) {
errorIfClosed();
checkTransactionSynchStatus();
return super.createStoredProcedureCall( procedureName, resultClasses );
}
public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
throws HibernateException {
errorIfClosed();

View File

@ -0,0 +1,555 @@
/*
* 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.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.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() ) ) {
if ( typeOfParameters == TypeOfParameter.POSITIONAL ) {
throw new QueryException( "Cannot mix named and positional parameters" );
}
typeOfParameters = TypeOfParameter.NAMED;
registeredParameters.add( parameter );
}
else if ( parameter.getPosition() != null ) {
if ( typeOfParameters == TypeOfParameter.NAMED ) {
throw new QueryException( "Cannot mix named and positional parameters" );
}
typeOfParameters = TypeOfParameter.POSITIONAL;
registeredParameters.add( parameter.getPosition(), parameter );
}
else {
throw new IllegalArgumentException( "Given parameter did not define name nor position [" + parameter + "]" );
}
}
@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 getRegisteredParameters() {
return 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;
}
/**
* 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() );
}
}
}
}
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) {
try {
if ( ProcedureParameterExtractionAware.class.isInstance( hibernateType ) ) {
return (T) ( (ProcedureParameterExtractionAware) hibernateType ).extract( statement, startIndex, session() );
}
else {
return (T) statement.getObject( startIndex );
}
}
catch (SQLException e) {
throw procedureCall.session().getFactory().getSQLExceptionHelper().convert(
e,
"Unable to extract OUT/INOUT parameter value"
);
}
}
}
public static class StoredProcedureParameterBindImpl<T> implements StoredProcedureParameterBind<T> {
private final T value;
private final TemporalType explicitTemporalType;
public StoredProcedureParameterBindImpl(T value) {
this( value, null );
}
public StoredProcedureParameterBindImpl(T value, TemporalType explicitTemporalType) {
this.value = value;
this.explicitTemporalType = explicitTemporalType;
}
@Override
public T getValue() {
return value;
}
@Override
public TemporalType getExplicitTemporalType() {
return explicitTemporalType;
}
}
public static class NamedStoredProcedureParameter<T> extends AbstractStoredProcedureParameterImpl<T> {
private final String name;
public NamedStoredProcedureParameter(
StoredProcedureCallImpl procedureCall,
String name,
ParameterMode mode,
Class<T> type) {
super( procedureCall, mode, type );
this.name = name;
}
@Override
public String getName() {
return name;
}
}
public static class PositionalStoredProcedureParameter<T> extends AbstractStoredProcedureParameterImpl<T> {
private final Integer position;
public PositionalStoredProcedureParameter(
StoredProcedureCallImpl procedureCall,
Integer position,
ParameterMode mode,
Class<T> type) {
super( procedureCall, mode, type );
this.position = position;
}
@Override
public Integer getPosition() {
return position;
}
}
}

View File

@ -0,0 +1,283 @@
/*
* 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 java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
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.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
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;
/**
* @author Steve Ebersole
*/
public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
private final StoredProcedureCallImpl procedureCall;
private final CallableStatement callableStatement;
private final CustomLoaderExtension loader;
private CurrentReturnDescriptor currentReturnDescriptor;
private boolean executed = false;
StoredProcedureOutputsImpl(StoredProcedureCallImpl procedureCall, CallableStatement callableStatement) {
this.procedureCall = procedureCall;
this.callableStatement = callableStatement;
// For now...
this.loader = buildSpecializedCustomLoader( procedureCall );
}
@Override
public Object getOutputParameterValue(String name) {
return procedureCall.getRegisteredParameter( name ).extract( callableStatement );
}
@Override
public Object getOutputParameterValue(int position) {
return procedureCall.getRegisteredParameter( position ).extract( callableStatement );
}
@Override
public boolean hasMoreReturns() {
if ( currentReturnDescriptor == null ) {
final boolean isResultSet;
if ( executed ) {
try {
isResultSet = callableStatement.getMoreResults();
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getMoreResults" );
}
}
else {
try {
isResultSet = callableStatement.execute();
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.execute" );
}
executed = true;
}
int updateCount = -1;
if ( ! isResultSet ) {
try {
updateCount = callableStatement.getUpdateCount();
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getUpdateCount" );
}
}
currentReturnDescriptor = new CurrentReturnDescriptor( isResultSet, updateCount );
}
return hasMoreResults( currentReturnDescriptor );
}
private boolean hasMoreResults(CurrentReturnDescriptor descriptor) {
return currentReturnDescriptor.isResultSet || currentReturnDescriptor.updateCount >= 0;
}
@Override
public StoredProcedureReturn getNextReturn() {
if ( currentReturnDescriptor == null ) {
if ( executed ) {
throw new IllegalStateException( "Unexpected condition" );
}
else {
throw new IllegalStateException( "hasMoreReturns() not called before getNextReturn()" );
}
}
if ( ! hasMoreResults( currentReturnDescriptor ) ) {
throw new IllegalStateException( "Results have been exhausted" );
}
CurrentReturnDescriptor copyReturnDescriptor = currentReturnDescriptor;
currentReturnDescriptor = null;
if ( copyReturnDescriptor.isResultSet ) {
try {
return new ResultSetReturn( this, callableStatement.getResultSet() );
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getResultSet" );
}
}
else {
return new UpdateCountReturn( this, copyReturnDescriptor.updateCount );
}
}
protected JDBCException convert(SQLException e, String message) {
return procedureCall.session().getFactory().getSQLExceptionHelper().convert( e, message, procedureCall.getProcedureName() );
}
private static class CurrentReturnDescriptor {
private final boolean isResultSet;
private final int updateCount;
private CurrentReturnDescriptor(boolean resultSet, int updateCount) {
isResultSet = resultSet;
this.updateCount = updateCount;
}
}
private static class ResultSetReturn implements StoredProcedureResultSetReturn {
private final StoredProcedureOutputsImpl storedProcedureOutputs;
private final ResultSet resultSet;
public ResultSetReturn(StoredProcedureOutputsImpl storedProcedureOutputs, ResultSet resultSet) {
this.storedProcedureOutputs = storedProcedureOutputs;
this.resultSet = resultSet;
}
@Override
public boolean isResultSet() {
return true;
}
@Override
@SuppressWarnings("unchecked")
public List getResultList() {
try {
return storedProcedureOutputs.loader.processResultSet( resultSet );
}
catch (SQLException e) {
throw storedProcedureOutputs.convert( e, "Error calling ResultSet.next" );
}
}
@Override
public Object getSingleResult() {
List results = getResultList();
if ( results == null || results.isEmpty() ) {
return null;
}
else {
return results.get( 0 );
}
}
}
private class UpdateCountReturn implements StoredProcedureUpdateCountReturn {
private final StoredProcedureOutputsImpl storedProcedureOutputs;
private final int updateCount;
public UpdateCountReturn(StoredProcedureOutputsImpl storedProcedureOutputs, int updateCount) {
this.storedProcedureOutputs = storedProcedureOutputs;
this.updateCount = updateCount;
}
@Override
public int getUpdateCount() {
return updateCount;
}
@Override
public boolean isResultSet() {
return false;
}
}
private static CustomLoaderExtension buildSpecializedCustomLoader(final StoredProcedureCallImpl procedureCall) {
final SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor(
procedureCall.getQueryReturns(),
procedureCall.session().getFactory()
);
processor.process();
final List<Return> customReturns = processor.generateCustomReturns( false );
CustomQuery customQuery = new CustomQuery() {
@Override
public String getSQL() {
return procedureCall.getProcedureName();
}
@Override
public Set<String> getQuerySpaces() {
return procedureCall.getSynchronizedQuerySpacesSet();
}
@Override
public Map getNamedParameterBindPoints() {
// no named parameters in terms of embedded in the SQL string
return null;
}
@Override
public List<Return> getCustomQueryReturns() {
return customReturns;
}
};
return new CustomLoaderExtension(
customQuery,
procedureCall.buildQueryParametersObject(),
procedureCall.session()
);
}
private static class CustomLoaderExtension extends CustomLoader {
private QueryParameters queryParameters;
private SessionImplementor session;
public CustomLoaderExtension(
CustomQuery customQuery,
QueryParameters queryParameters,
SessionImplementor session) {
super( customQuery, session.getFactory() );
this.queryParameters = queryParameters;
this.session = session;
}
public List processResultSet(ResultSet resultSet) throws SQLException {
super.autoDiscoverTypes( resultSet );
return super.processResultSet(
resultSet,
queryParameters,
session,
true,
null,
Integer.MAX_VALUE
);
}
}
}

View File

@ -28,6 +28,7 @@ import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -668,12 +669,9 @@ public abstract class Loader {
applyPostLoadLocks( row, lockModesArray, session );
return forcedResultTransformer == null ?
getResultColumnOrRow( row, queryParameters.getResultTransformer(), resultSet, session ) :
forcedResultTransformer.transformTuple(
getResultRow( row, resultSet, session ),
getResultRowAliases()
)
return forcedResultTransformer == null
? getResultColumnOrRow( row, queryParameters.getResultTransformer(), resultSet, session )
: forcedResultTransformer.transformTuple( getResultRow( row, resultSet, session ), getResultRowAliases() )
;
}
@ -825,11 +823,8 @@ public abstract class Loader {
selection.getMaxRows() :
Integer.MAX_VALUE;
final int entitySpan = getEntityPersisters().length;
final ArrayList hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan * 10 );
final ResultSet rs = executeQueryStatement( queryParameters, false, session );
final PreparedStatement st = (PreparedStatement) rs.getStatement();
final Statement st = rs.getStatement();
// would be great to move all this below here into another method that could also be used
// from the new scrolling stuff.
@ -837,47 +832,59 @@ public abstract class Loader {
// Would need to change the way the max-row stuff is handled (i.e. behind an interface) so
// that I could do the control breaking at the means to know when to stop
final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );
final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() );
final boolean createSubselects = isSubselectLoadingEnabled();
final List subselectResultKeys = createSubselects ? new ArrayList() : null;
final List results = new ArrayList();
try {
handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session );
EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row
LOG.trace( "Processing result set" );
int count;
for ( count = 0; count < maxRows && rs.next(); count++ ) {
LOG.debugf( "Result set row: %s", count );
Object result = getRowFromResultSet(
rs,
session,
queryParameters,
lockModesArray,
optionalObjectKey,
hydratedObjects,
keys,
returnProxies,
forcedResultTransformer
);
results.add( result );
if ( createSubselects ) {
subselectResultKeys.add(keys);
keys = new EntityKey[entitySpan]; //can't reuse in this case
}
}
LOG.tracev( "Done processing result set ({0} rows)", count );
return processResultSet( rs, queryParameters, session, returnProxies, forcedResultTransformer, maxRows );
}
finally {
st.close();
}
}
protected List processResultSet(
ResultSet rs,
QueryParameters queryParameters,
SessionImplementor session,
boolean returnProxies,
ResultTransformer forcedResultTransformer,
int maxRows) throws SQLException {
final int entitySpan = getEntityPersisters().length;
final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );
final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() );
final boolean createSubselects = isSubselectLoadingEnabled();
final List subselectResultKeys = createSubselects ? new ArrayList() : null;
final ArrayList hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan * 10 );
final List results = new ArrayList();
handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session );
EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row
LOG.trace( "Processing result set" );
int count;
for ( count = 0; count < maxRows && rs.next(); count++ ) {
LOG.debugf( "Result set row: %s", count );
Object result = getRowFromResultSet(
rs,
session,
queryParameters,
lockModesArray,
optionalObjectKey,
hydratedObjects,
keys,
returnProxies,
forcedResultTransformer
);
results.add( result );
if ( createSubselects ) {
subselectResultKeys.add(keys);
keys = new EntityKey[entitySpan]; //can't reuse in this case
}
}
LOG.tracev( "Done processing result set ({0} rows)", count );
initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly( session ) );
if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session );
return results;
}
protected boolean isSubselectLoadingEnabled() {
@ -1060,8 +1067,11 @@ public abstract class Loader {
* This empty implementation merely returns its first argument. This is
* overridden by some subclasses.
*/
protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
throws SQLException, HibernateException {
protected Object getResultColumnOrRow(
Object[] row,
ResultTransformer transformer,
ResultSet rs,
SessionImplementor session) throws SQLException, HibernateException {
return row;
}
@ -1069,10 +1079,10 @@ public abstract class Loader {
return null;
}
protected Object[] getResultRow(Object[] row,
ResultSet rs,
SessionImplementor session)
throws SQLException, HibernateException {
protected Object[] getResultRow(
Object[] row,
ResultSet rs,
SessionImplementor session) throws SQLException, HibernateException {
return row;
}
@ -1455,7 +1465,7 @@ public abstract class Loader {
persister,
key.getIdentifier(),
session
);
);
final Object object;
if ( optionalObjectKey != null && key.equals( optionalObjectKey ) ) {
@ -1687,7 +1697,10 @@ public abstract class Loader {
queryParameters.processFilters( getSQLString(), session );
// Applying LIMIT clause.
final LimitHandler limitHandler = getLimitHandler( queryParameters.getFilteredSQL(), queryParameters.getRowSelection() );
final LimitHandler limitHandler = getLimitHandler(
queryParameters.getFilteredSQL(),
queryParameters.getRowSelection()
);
String sql = limitHandler.getProcessedSql();
// Adding locks and comments.

View File

@ -50,7 +50,7 @@ public interface CustomQuery {
*
* @return The query spaces
*/
public Set getQuerySpaces();
public Set<String> getQuerySpaces();
/**
* A map representing positions within the supplied {@link #getSQL query} to
@ -73,5 +73,5 @@ public interface CustomQuery {
*
* @return List of return descriptors.
*/
public List getCustomQueryReturns();
public List<Return> getCustomQueryReturns();
}

View File

@ -156,49 +156,49 @@ public class SQLQueryReturnProcessor {
public ResultAliasContext process() {
// first, break down the returns into maps keyed by alias
// so that role returns can be more easily resolved to their owners
for ( int i = 0; i < queryReturns.length; i++ ) {
if ( queryReturns[i] instanceof NativeSQLQueryNonScalarReturn ) {
NativeSQLQueryNonScalarReturn rtn = ( NativeSQLQueryNonScalarReturn ) queryReturns[i];
for ( NativeSQLQueryReturn queryReturn : queryReturns ) {
if ( queryReturn instanceof NativeSQLQueryNonScalarReturn ) {
NativeSQLQueryNonScalarReturn rtn = (NativeSQLQueryNonScalarReturn) queryReturn;
alias2Return.put( rtn.getAlias(), rtn );
if ( rtn instanceof NativeSQLQueryJoinReturn ) {
NativeSQLQueryJoinReturn fetchReturn = ( NativeSQLQueryJoinReturn ) rtn;
NativeSQLQueryJoinReturn fetchReturn = (NativeSQLQueryJoinReturn) rtn;
alias2OwnerAlias.put( fetchReturn.getAlias(), fetchReturn.getOwnerAlias() );
}
}
}
// Now, process the returns
for ( int i = 0; i < queryReturns.length; i++ ) {
processReturn( queryReturns[i] );
for ( NativeSQLQueryReturn queryReturn : queryReturns ) {
processReturn( queryReturn );
}
return new ResultAliasContext();
}
public List generateCustomReturns(boolean queryHadAliases) {
List customReturns = new ArrayList();
Map customReturnsByAlias = new HashMap();
for ( int i = 0; i < queryReturns.length; i++ ) {
if ( queryReturns[i] instanceof NativeSQLQueryScalarReturn ) {
NativeSQLQueryScalarReturn rtn = ( NativeSQLQueryScalarReturn ) queryReturns[i];
public List<Return> generateCustomReturns(boolean queryHadAliases) {
List<Return> customReturns = new ArrayList<Return>();
Map<String,Return> customReturnsByAlias = new HashMap<String,Return>();
for ( NativeSQLQueryReturn queryReturn : queryReturns ) {
if ( queryReturn instanceof NativeSQLQueryScalarReturn ) {
NativeSQLQueryScalarReturn rtn = (NativeSQLQueryScalarReturn) queryReturn;
customReturns.add( new ScalarReturn( rtn.getType(), rtn.getColumnAlias() ) );
}
else if ( queryReturns[i] instanceof NativeSQLQueryRootReturn ) {
NativeSQLQueryRootReturn rtn = ( NativeSQLQueryRootReturn ) queryReturns[i];
else if ( queryReturn instanceof NativeSQLQueryRootReturn ) {
NativeSQLQueryRootReturn rtn = (NativeSQLQueryRootReturn) queryReturn;
String alias = rtn.getAlias();
EntityAliases entityAliases;
if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
entityAliases = new DefaultEntityAliases(
( Map ) entityPropertyResultMaps.get( alias ),
( SQLLoadable ) alias2Persister.get( alias ),
( String ) alias2Suffix.get( alias )
(Map) entityPropertyResultMaps.get( alias ),
(SQLLoadable) alias2Persister.get( alias ),
(String) alias2Suffix.get( alias )
);
}
else {
entityAliases = new ColumnEntityAliases(
( Map ) entityPropertyResultMaps.get( alias ),
( SQLLoadable ) alias2Persister.get( alias ),
( String ) alias2Suffix.get( alias )
(Map) entityPropertyResultMaps.get( alias ),
(SQLLoadable) alias2Persister.get( alias ),
(String) alias2Suffix.get( alias )
);
}
RootReturn customReturn = new RootReturn(
@ -210,37 +210,37 @@ public class SQLQueryReturnProcessor {
customReturns.add( customReturn );
customReturnsByAlias.put( rtn.getAlias(), customReturn );
}
else if ( queryReturns[i] instanceof NativeSQLQueryCollectionReturn ) {
NativeSQLQueryCollectionReturn rtn = ( NativeSQLQueryCollectionReturn ) queryReturns[i];
else if ( queryReturn instanceof NativeSQLQueryCollectionReturn ) {
NativeSQLQueryCollectionReturn rtn = (NativeSQLQueryCollectionReturn) queryReturn;
String alias = rtn.getAlias();
SQLLoadableCollection persister = ( SQLLoadableCollection ) alias2CollectionPersister.get( alias );
SQLLoadableCollection persister = (SQLLoadableCollection) alias2CollectionPersister.get( alias );
boolean isEntityElements = persister.getElementType().isEntityType();
CollectionAliases collectionAliases;
EntityAliases elementEntityAliases = null;
if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
collectionAliases = new GeneratedCollectionAliases(
( Map ) collectionPropertyResultMaps.get( alias ),
( SQLLoadableCollection ) alias2CollectionPersister.get( alias ),
( String ) alias2CollectionSuffix.get( alias )
(Map) collectionPropertyResultMaps.get( alias ),
(SQLLoadableCollection) alias2CollectionPersister.get( alias ),
(String) alias2CollectionSuffix.get( alias )
);
if ( isEntityElements ) {
elementEntityAliases = new DefaultEntityAliases(
( Map ) entityPropertyResultMaps.get( alias ),
( SQLLoadable ) alias2Persister.get( alias ),
( String ) alias2Suffix.get( alias )
(Map) entityPropertyResultMaps.get( alias ),
(SQLLoadable) alias2Persister.get( alias ),
(String) alias2Suffix.get( alias )
);
}
}
else {
collectionAliases = new ColumnCollectionAliases(
( Map ) collectionPropertyResultMaps.get( alias ),
( SQLLoadableCollection ) alias2CollectionPersister.get( alias )
(Map) collectionPropertyResultMaps.get( alias ),
(SQLLoadableCollection) alias2CollectionPersister.get( alias )
);
if ( isEntityElements ) {
elementEntityAliases = new ColumnEntityAliases(
( Map ) entityPropertyResultMaps.get( alias ),
( SQLLoadable ) alias2Persister.get( alias ),
( String ) alias2Suffix.get( alias )
(Map) entityPropertyResultMaps.get( alias ),
(SQLLoadable) alias2Persister.get( alias ),
(String) alias2Suffix.get( alias )
);
}
}
@ -249,46 +249,46 @@ public class SQLQueryReturnProcessor {
rtn.getOwnerEntityName(),
rtn.getOwnerProperty(),
collectionAliases,
elementEntityAliases,
elementEntityAliases,
rtn.getLockMode()
);
customReturns.add( customReturn );
customReturnsByAlias.put( rtn.getAlias(), customReturn );
}
else if ( queryReturns[i] instanceof NativeSQLQueryJoinReturn ) {
NativeSQLQueryJoinReturn rtn = ( NativeSQLQueryJoinReturn ) queryReturns[i];
else if ( queryReturn instanceof NativeSQLQueryJoinReturn ) {
NativeSQLQueryJoinReturn rtn = (NativeSQLQueryJoinReturn) queryReturn;
String alias = rtn.getAlias();
FetchReturn customReturn;
NonScalarReturn ownerCustomReturn = ( NonScalarReturn ) customReturnsByAlias.get( rtn.getOwnerAlias() );
NonScalarReturn ownerCustomReturn = (NonScalarReturn) customReturnsByAlias.get( rtn.getOwnerAlias() );
if ( alias2CollectionPersister.containsKey( alias ) ) {
SQLLoadableCollection persister = ( SQLLoadableCollection ) alias2CollectionPersister.get( alias );
SQLLoadableCollection persister = (SQLLoadableCollection) alias2CollectionPersister.get( alias );
boolean isEntityElements = persister.getElementType().isEntityType();
CollectionAliases collectionAliases;
EntityAliases elementEntityAliases = null;
if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
collectionAliases = new GeneratedCollectionAliases(
( Map ) collectionPropertyResultMaps.get( alias ),
(Map) collectionPropertyResultMaps.get( alias ),
persister,
( String ) alias2CollectionSuffix.get( alias )
(String) alias2CollectionSuffix.get( alias )
);
if ( isEntityElements ) {
elementEntityAliases = new DefaultEntityAliases(
( Map ) entityPropertyResultMaps.get( alias ),
( SQLLoadable ) alias2Persister.get( alias ),
( String ) alias2Suffix.get( alias )
(Map) entityPropertyResultMaps.get( alias ),
(SQLLoadable) alias2Persister.get( alias ),
(String) alias2Suffix.get( alias )
);
}
}
else {
collectionAliases = new ColumnCollectionAliases(
( Map ) collectionPropertyResultMaps.get( alias ),
(Map) collectionPropertyResultMaps.get( alias ),
persister
);
if ( isEntityElements ) {
elementEntityAliases = new ColumnEntityAliases(
( Map ) entityPropertyResultMaps.get( alias ),
( SQLLoadable ) alias2Persister.get( alias ),
( String ) alias2Suffix.get( alias )
(Map) entityPropertyResultMaps.get( alias ),
(SQLLoadable) alias2Persister.get( alias ),
(String) alias2Suffix.get( alias )
);
}
}
@ -297,7 +297,7 @@ public class SQLQueryReturnProcessor {
ownerCustomReturn,
rtn.getOwnerProperty(),
collectionAliases,
elementEntityAliases,
elementEntityAliases,
rtn.getLockMode()
);
}
@ -305,16 +305,16 @@ public class SQLQueryReturnProcessor {
EntityAliases entityAliases;
if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
entityAliases = new DefaultEntityAliases(
( Map ) entityPropertyResultMaps.get( alias ),
( SQLLoadable ) alias2Persister.get( alias ),
( String ) alias2Suffix.get( alias )
(Map) entityPropertyResultMaps.get( alias ),
(SQLLoadable) alias2Persister.get( alias ),
(String) alias2Suffix.get( alias )
);
}
else {
entityAliases = new ColumnEntityAliases(
( Map ) entityPropertyResultMaps.get( alias ),
( SQLLoadable ) alias2Persister.get( alias ),
( String ) alias2Suffix.get( alias )
(Map) entityPropertyResultMaps.get( alias ),
(SQLLoadable) alias2Persister.get( alias ),
(String) alias2Suffix.get( alias )
);
}
customReturn = new EntityFetchReturn(
@ -378,10 +378,6 @@ public class SQLQueryReturnProcessor {
addPersister( rootReturn.getAlias(), rootReturn.getPropertyResultsMap(), persister );
}
/**
* @param propertyResult
* @param persister
*/
private void addPersister(String alias, Map propertyResult, SQLLoadable persister) {
alias2Persister.put( alias, persister );
String suffix = generateEntitySuffix();

View File

@ -24,6 +24,7 @@
package org.hibernate.type;
import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -48,12 +49,12 @@ import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* TODO : javadoc
* Convenience base class for {@link BasicType} implementations
*
* @author Steve Ebersole
*/
public abstract class AbstractStandardBasicType<T>
implements BasicType, StringRepresentableType<T>, XmlRepresentableType<T> {
implements BasicType, StringRepresentableType<T>, XmlRepresentableType<T>, ProcedureParameterExtractionAware<T> {
private static final Size DEFAULT_SIZE = new Size( 19, 2, 255, Size.LobMultiplier.NONE ); // to match legacy behavior
private final Size dictatedSize = new Size();
@ -386,4 +387,32 @@ public abstract class AbstractStandardBasicType<T>
? getReplacement( (T) original, (T) target, session )
: target;
}
@Override
public boolean canDoExtraction() {
return true;
}
@Override
public T extract(CallableStatement statement, int startIndex, final SessionImplementor session) throws SQLException {
// todo : have SessionImplementor extend WrapperOptions
final WrapperOptions options = new WrapperOptions() {
public boolean useStreamForLobBinding() {
return Environment.useStreamsForBinary();
}
public LobCreator getLobCreator() {
return Hibernate.getLobCreator( session );
}
public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
final SqlTypeDescriptor remapped = sqlTypeDescriptor.canBeRemapped()
? session.getFactory().getDialect().remapSqlTypeDescriptor( sqlTypeDescriptor )
: sqlTypeDescriptor;
return remapped == null ? sqlTypeDescriptor : remapped;
}
};
return remapSqlTypeDescriptor( options ).getExtractor( javaTypeDescriptor ).extract( statement, startIndex, options );
}
}

View File

@ -25,6 +25,7 @@ package org.hibernate.type;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -55,7 +56,7 @@ import org.hibernate.tuple.component.ComponentTuplizer;
*
* @author Gavin King
*/
public class ComponentType extends AbstractType implements CompositeType {
public class ComponentType extends AbstractType implements CompositeType, ProcedureParameterExtractionAware {
private final TypeFactory.TypeScope typeScope;
private final String[] propertyNames;
@ -720,4 +721,55 @@ public class ComponentType extends AbstractType implements CompositeType {
"Unable to locate property named " + name + " on " + getReturnedClass().getName()
);
}
private Boolean canDoExtraction;
@Override
public boolean canDoExtraction() {
if ( canDoExtraction == null ) {
canDoExtraction = determineIfProcedureParamExtractionCanBePerformed();
}
return canDoExtraction;
}
private boolean determineIfProcedureParamExtractionCanBePerformed() {
for ( Type propertyType : propertyTypes ) {
if ( ! ProcedureParameterExtractionAware.class.isInstance( propertyType ) ) {
return false;
}
if ( ! ( (ProcedureParameterExtractionAware) propertyType ).canDoExtraction() ) {
return false;
}
}
return true;
}
@Override
public Object extract(CallableStatement statement, int startIndex, SessionImplementor session) throws SQLException {
Object[] values = new Object[propertySpan];
int currentIndex = startIndex;
boolean notNull = false;
for ( int i = 0; i < propertySpan; i++ ) {
// we know this cast is safe from canDoExtraction
final ProcedureParameterExtractionAware propertyType = (ProcedureParameterExtractionAware) propertyTypes[i];
final Object value = propertyType.extract( statement, currentIndex, session );
if ( value == null ) {
if ( isKey ) {
return null; //different nullability rules for pk/fk
}
}
else {
notNull = true;
}
values[i] = value;
currentIndex += propertyType.getColumnSpan( session.getFactory() );
}
if ( ! notNull ) {
values = null;
}
return resolve( values, session, null );
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -84,6 +85,11 @@ public class PostgresUUIDType extends AbstractSingleColumnStandardBasicType<UUID
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getObject( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getObject( index ), options );
}
};
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.type;
import java.sql.CallableStatement;
import java.sql.SQLException;
import org.hibernate.engine.spi.SessionImplementor;
/**
* Optional {@link Type} contract for implementations that are aware of how to extract values from
* store procedure OUT/INOUT parameters.
*
* @author Steve Ebersole
*/
public interface ProcedureParameterExtractionAware<T> extends Type {
/**
* Can the given instance of this type actually perform the parameter value extractions?
*
* @return {@code true} indicates that @{link #extract} calls will not fail due to {@link IllegalStateException}.
*/
public boolean canDoExtraction();
/**
* Perform the extraction
*
* @param statement The CallableStatement from which to extract the parameter value(s).
* @param startIndex The parameter index from which to start extracting; assumes the values (if multiple) are contiguous
* @param session The originating session
*
* @return The extracted value.
*
* @throws SQLException Indicates an issue calling into the CallableStatement
* @throws IllegalStateException Thrown if this method is called on instances that return {@code false} for {@link #canDoExtraction}
*/
public T extract(CallableStatement statement, int startIndex, SessionImplementor session) throws SQLException;
}

View File

@ -22,11 +22,13 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Contract for extracting a value from a {@link ResultSet}.
* Contract for extracting value via JDBC (from {@link ResultSet} or as output param from {@link CallableStatement}).
*
* @author Steve Ebersole
*/
@ -43,4 +45,6 @@ public interface ValueExtractor<X> {
* @throws SQLException Indicates a JDBC error occurred.
*/
public X extract(ResultSet rs, String name, WrapperOptions options) throws SQLException;
public X extract(CallableStatement rs, int index, WrapperOptions options) throws SQLException;
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -58,9 +59,7 @@ public abstract class BasicExtractor<J> implements ValueExtractor<J> {
return sqlDescriptor;
}
/**
* {@inheritDoc}
*/
@Override
public J extract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
final J value = doExtract( rs, name, options );
if ( value == null || rs.wasNull() ) {
@ -90,4 +89,35 @@ public abstract class BasicExtractor<J> implements ValueExtractor<J> {
* @throws SQLException Indicates a problem access the result set
*/
protected abstract J doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException;
@Override
public J extract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
final J value = doExtract( statement, index, options );
if ( value == null || statement.wasNull() ) {
LOG.tracev( "Found [null] as procedure output parameter [{0}]", index );
return null;
}
else {
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Found [{0}] as procedure output parameter [{1}]", getJavaDescriptor().extractLoggableRepresentation( value ), index );
}
return value;
}
}
/**
* Perform the extraction.
* <p/>
* Called from {@link #extract}. Null checking of the value (as well as consulting {@link ResultSet#wasNull}) is
* done there.
*
* @param statement The callable statement containing the output parameter
* @param index The index (position) of the output parameter
* @param options The binding options
*
* @return The extracted value.
*
* @throws SQLException Indicates a problem accessing the parameter value
*/
protected abstract J doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException;
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -65,6 +66,11 @@ public class BigIntTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getLong( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getLong( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -68,6 +69,11 @@ public class BitTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getBoolean( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getBoolean( index ), options );
}
};
}
}

View File

@ -24,6 +24,7 @@
package org.hibernate.type.descriptor.sql;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -117,6 +118,11 @@ public abstract class BlobTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getBlob( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getBlob( index ), options );
}
};
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -65,6 +66,11 @@ public class BooleanTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getBoolean( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getBoolean( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@ -107,6 +108,11 @@ public abstract class ClobTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getClob( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getClob( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@ -66,6 +67,11 @@ public class DateTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getDate( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getDate( index ), options );
}
};
}
}

View File

@ -24,6 +24,7 @@
package org.hibernate.type.descriptor.sql;
import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -66,6 +67,11 @@ public class DecimalTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getBigDecimal( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getBigDecimal( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -65,6 +66,11 @@ public class DoubleTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getDouble( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getDouble( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -65,6 +66,11 @@ public class IntegerTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getInt( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getInt( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -65,6 +66,11 @@ public class RealTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getFloat( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getFloat( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -65,6 +66,11 @@ public class SmallIntTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getShort( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getShort( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -66,6 +67,11 @@ public class TimeTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getTime( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getTime( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -66,6 +67,11 @@ public class TimestampTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getTimestamp( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getTimestamp( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -68,6 +69,11 @@ public class TinyIntTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getByte( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getByte( index ), options );
}
};
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -63,8 +64,12 @@ public class VarbinaryTypeDescriptor implements SqlTypeDescriptor {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
final byte[] bytes = rs.getBytes( name );
return javaTypeDescriptor.wrap( bytes, options );
return javaTypeDescriptor.wrap( rs.getBytes( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getBytes( index ), options );
}
};
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -65,6 +66,11 @@ public class VarcharTypeDescriptor implements SqlTypeDescriptor {
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getString( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getString( index ), options );
}
};
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.test.sql.storedproc;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.SQLQuery;
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.junit.Before;
import org.junit.Test;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.junit4.ExtraAssertions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @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";
// }
// }
// );
// }
@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();
assertNotNull( nextReturn );
ExtraAssertions.assertClassAssignability( StoredProcedureResultSetReturn.class, nextReturn.getClass() );
StoredProcedureResultSetReturn resultSetReturn = (StoredProcedureResultSetReturn) nextReturn;
String name = (String) resultSetReturn.getSingleResult();
assertEquals( "SA", name );
session.getTransaction().commit();
session.close();
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.test.typeoverride;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -73,6 +74,16 @@ public class StoredPrefixedStringType
}
return javaTypeDescriptor.wrap( stringValue.substring( PREFIX.length() ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options)
throws SQLException {
String stringValue = statement.getString( index );
if ( ! stringValue.startsWith( PREFIX ) ) {
throw new AssertionFailure( "Value read from procedure output param does not have prefix." );
}
return javaTypeDescriptor.wrap( stringValue.substring( PREFIX.length() ), options );
}
};
}
};

View File

@ -41,6 +41,7 @@ import javax.persistence.PessimisticLockException;
import javax.persistence.PessimisticLockScope;
import javax.persistence.Query;
import javax.persistence.QueryTimeoutException;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.TransactionRequiredException;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
@ -81,10 +82,12 @@ import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.StoredProcedureCall;
import org.hibernate.TransientObjectException;
import org.hibernate.TypeMismatchException;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.dialect.lock.LockingStrategyException;
import org.hibernate.dialect.lock.OptimisticEntityLockException;
import org.hibernate.dialect.lock.PessimisticEntityLockException;
@ -776,6 +779,38 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
}
}
@Override
public StoredProcedureQuery createNamedStoredProcedureQuery(String name) {
throw new NotYetImplementedException();
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
try {
StoredProcedureCall call = getSession().createStoredProcedureCall( procedureName );
return new StoredProcedureQueryImpl( call, this );
}
catch ( HibernateException he ) {
throw convert( he );
}
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) {
try {
StoredProcedureCall call = getSession().createStoredProcedureCall( procedureName, resultClasses );
return new StoredProcedureQueryImpl( call, this );
}
catch ( HibernateException he ) {
throw convert( he );
}
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) {
throw new NotYetImplementedException();
}
@SuppressWarnings("unchecked")
public <T> T getReference(Class<T> entityClass, Object primaryKey) {
try {

View File

@ -0,0 +1,602 @@
/*
* 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.ejb;
import javax.persistence.CacheRetrieveMode;
import javax.persistence.CacheStoreMode;
import javax.persistence.FlushModeType;
import javax.persistence.Parameter;
import javax.persistence.Query;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jboss.logging.Logger;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.LockMode;
import org.hibernate.ejb.internal.EntityManagerMessageLogger;
import org.hibernate.ejb.util.CacheModeHelper;
import org.hibernate.ejb.util.ConfigurationHelper;
import org.hibernate.ejb.util.LockModeTypeHelper;
import static org.hibernate.ejb.QueryHints.HINT_CACHEABLE;
import static org.hibernate.ejb.QueryHints.HINT_CACHE_MODE;
import static org.hibernate.ejb.QueryHints.HINT_CACHE_REGION;
import static org.hibernate.ejb.QueryHints.HINT_COMMENT;
import static org.hibernate.ejb.QueryHints.HINT_FETCH_SIZE;
import static org.hibernate.ejb.QueryHints.HINT_FLUSH_MODE;
import static org.hibernate.ejb.QueryHints.HINT_READONLY;
import static org.hibernate.ejb.QueryHints.HINT_TIMEOUT;
import static org.hibernate.ejb.QueryHints.SPEC_HINT_TIMEOUT;
/**
* Intended as the base class for all {@link Query} implementations, including {@link TypedQuery} and
* {@link javax.persistence.StoredProcedureQuery}. Care should be taken that all changes here fit with all
* those usages.
*
* @author Steve Ebersole
*/
public abstract class BaseQueryImpl implements Query {
private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(
EntityManagerMessageLogger.class,
AbstractQueryImpl.class.getName()
);
private final HibernateEntityManagerImplementor entityManager;
private int firstResult;
private int maxResults = -1;
private Map<String, Object> hints;
public BaseQueryImpl(HibernateEntityManagerImplementor entityManager) {
this.entityManager = entityManager;
}
protected HibernateEntityManagerImplementor entityManager() {
return entityManager;
}
// Limits (first and max results) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Apply the given first-result value.
*
* @param firstResult The specified first-result value.
*/
protected abstract void applyFirstResult(int firstResult);
@Override
public BaseQueryImpl setFirstResult(int firstResult) {
if ( firstResult < 0 ) {
throw new IllegalArgumentException(
"Negative value (" + firstResult + ") passed to setFirstResult"
);
}
this.firstResult = firstResult;
applyFirstResult( firstResult );
return this;
}
@Override
public int getFirstResult() {
return firstResult;
}
/**
* Apply the given max results value.
*
* @param maxResults The specified max results
*/
protected abstract void applyMaxResults(int maxResults);
@Override
public BaseQueryImpl setMaxResults(int maxResult) {
if ( maxResult < 0 ) {
throw new IllegalArgumentException(
"Negative value (" + maxResult + ") passed to setMaxResults"
);
}
this.maxResults = maxResult;
applyMaxResults( maxResult );
return this;
}
public int getSpecifiedMaxResults() {
return maxResults;
}
@Override
public int getMaxResults() {
return maxResults == -1
? Integer.MAX_VALUE // stupid spec... MAX_VALUE??
: maxResults;
}
// Hints ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@SuppressWarnings( {"UnusedDeclaration"})
public Set<String> getSupportedHints() {
return QueryHints.getDefinedHints();
}
@Override
public Map<String, Object> getHints() {
return hints;
}
/**
* Apply the query timeout hint.
*
* @param timeout The timeout (in seconds!) specified as a hint
*
* @return {@code true} if the hint was "applied"
*/
protected abstract boolean applyTimeoutHint(int timeout);
/**
* Apply the lock timeout (in seconds!) hint
*
* @param timeout The timeout (in seconds!) specified as a hint
*
* @return {@code true} if the hint was "applied"
*/
protected abstract boolean applyLockTimeoutHint(int timeout);
/**
* Apply the comment hint.
*
* @param comment The comment specified as a hint
*
* @return {@code true} if the hint was "applied"
*/
protected abstract boolean applyCommentHint(String comment);
/**
* Apply the fetch size hint
*
* @param fetchSize The fetch size specified as a hint
*
* @return {@code true} if the hint was "applied"
*/
protected abstract boolean applyFetchSize(int fetchSize);
/**
* Apply the cacheable (true/false) hint.
*
* @param isCacheable The value specified as hint
*
* @return {@code true} if the hint was "applied"
*/
protected abstract boolean applyCacheableHint(boolean isCacheable);
/**
* Apply the cache region hint
*
* @param regionName The name of the cache region specified as a hint
*
* @return {@code true} if the hint was "applied"
*/
protected abstract boolean applyCacheRegionHint(String regionName);
/**
* Apply the read-only (true/false) hint.
*
* @param isReadOnly The value specified as hint
*
* @return {@code true} if the hint was "applied"
*/
protected abstract boolean applyReadOnlyHint(boolean isReadOnly);
/**
* Apply the CacheMode hint.
*
* @param cacheMode The CacheMode value specified as a hint.
*
* @return {@code true} if the hint was "applied"
*/
protected abstract boolean applyCacheModeHint(CacheMode cacheMode);
/**
* Apply the FlushMode hint.
*
* @param flushMode The FlushMode value specified as hint
*
* @return {@code true} if the hint was "applied"
*/
protected abstract boolean applyFlushModeHint(FlushMode flushMode);
/**
* Can alias-specific lock modes be applied?
*
* @return {@code true} indicates they can be applied, {@code false} otherwise.
*/
protected abstract boolean canApplyLockModesHints();
/**
* Apply the alias specific lock modes. Assumes {@link #canApplyLockModesHints()} has already been called and
* returned {@code true}.
*
* @param alias The alias to apply the 'lockMode' to.
* @param lockMode The LockMode to apply.
*/
protected abstract void applyAliasSpecificLockModeHint(String alias, LockMode lockMode);
@Override
@SuppressWarnings( {"deprecation"})
public BaseQueryImpl setHint(String hintName, Object value) {
boolean applied = false;
try {
if ( HINT_TIMEOUT.equals( hintName ) ) {
applied = applyTimeoutHint( ConfigurationHelper.getInteger( value ) );
}
else if ( SPEC_HINT_TIMEOUT.equals( hintName ) ) {
// convert milliseconds to seconds
int timeout = (int)Math.round(ConfigurationHelper.getInteger( value ).doubleValue() / 1000.0 );
applied = applyTimeoutHint( timeout );
}
else if ( AvailableSettings.LOCK_TIMEOUT.equals( hintName ) ) {
applied = applyLockTimeoutHint( ConfigurationHelper.getInteger( value ) );
}
else if ( HINT_COMMENT.equals( hintName ) ) {
applied = applyCommentHint( (String) value );
}
else if ( HINT_FETCH_SIZE.equals( hintName ) ) {
applied = applyFetchSize( ConfigurationHelper.getInteger( value ) );
}
else if ( HINT_CACHEABLE.equals( hintName ) ) {
applied = applyCacheableHint( ConfigurationHelper.getBoolean( value ) );
}
else if ( HINT_CACHE_REGION.equals( hintName ) ) {
applied = applyCacheRegionHint( (String) value );
}
else if ( HINT_READONLY.equals( hintName ) ) {
applied = applyReadOnlyHint( ConfigurationHelper.getBoolean( value ) );
}
else if ( HINT_CACHE_MODE.equals( hintName ) ) {
applied = applyCacheModeHint( ConfigurationHelper.getCacheMode( value ) );
}
else if ( HINT_FLUSH_MODE.equals( hintName ) ) {
applied = applyFlushModeHint( ConfigurationHelper.getFlushMode( value ) );
}
else if ( AvailableSettings.SHARED_CACHE_RETRIEVE_MODE.equals( hintName ) ) {
final CacheRetrieveMode retrieveMode = (CacheRetrieveMode) value;
CacheStoreMode storeMode = hints != null
? (CacheStoreMode) hints.get( AvailableSettings.SHARED_CACHE_STORE_MODE )
: null;
if ( storeMode == null ) {
storeMode = (CacheStoreMode) entityManager.getProperties().get( AvailableSettings.SHARED_CACHE_STORE_MODE );
}
applied = applyCacheModeHint( CacheModeHelper.interpretCacheMode( storeMode, retrieveMode ) );
}
else if ( AvailableSettings.SHARED_CACHE_STORE_MODE.equals( hintName ) ) {
final CacheStoreMode storeMode = (CacheStoreMode) value;
CacheRetrieveMode retrieveMode = hints != null
? (CacheRetrieveMode) hints.get( AvailableSettings.SHARED_CACHE_RETRIEVE_MODE )
: null;
if ( retrieveMode == null ) {
retrieveMode = (CacheRetrieveMode) entityManager.getProperties().get( AvailableSettings.SHARED_CACHE_RETRIEVE_MODE );
}
applied = applyCacheModeHint(
CacheModeHelper.interpretCacheMode( storeMode, retrieveMode )
);
}
else if ( hintName.startsWith( AvailableSettings.ALIAS_SPECIFIC_LOCK_MODE ) ) {
if ( canApplyLockModesHints() ) {
// extract the alias
final String alias = hintName.substring( AvailableSettings.ALIAS_SPECIFIC_LOCK_MODE.length() + 1 );
// determine the LockMode
try {
final LockMode lockMode = LockModeTypeHelper.interpretLockMode( value );
applyAliasSpecificLockModeHint( alias, lockMode );
}
catch ( Exception e ) {
LOG.unableToDetermineLockModeValue( hintName, value );
applied = false;
}
}
else {
applied = false;
}
}
else {
LOG.ignoringUnrecognizedQueryHint( hintName );
}
}
catch ( ClassCastException e ) {
throw new IllegalArgumentException( "Value for hint" );
}
if ( applied ) {
if ( hints == null ) {
hints = new HashMap<String,Object>();
}
hints.put( hintName, value );
}
else {
LOG.debugf( "Skipping unsupported query hint [%s]", hintName );
}
return this;
}
// FlushMode ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private FlushModeType jpaFlushMode;
@Override
public BaseQueryImpl setFlushMode(FlushModeType jpaFlushMode) {
this.jpaFlushMode = jpaFlushMode;
// TODO : treat as hint?
if ( jpaFlushMode == FlushModeType.AUTO ) {
applyFlushModeHint( FlushMode.AUTO );
}
else if ( jpaFlushMode == FlushModeType.COMMIT ) {
applyFlushModeHint( FlushMode.COMMIT );
}
return this;
}
@SuppressWarnings( {"UnusedDeclaration"})
protected FlushModeType getSpecifiedFlushMode() {
return jpaFlushMode;
}
@Override
public FlushModeType getFlushMode() {
return jpaFlushMode != null
? jpaFlushMode
: entityManager.getFlushMode();
}
// Parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private List<ParameterImplementor> parameters;
/**
* Hibernate specific extension to the JPA {@link Parameter} contract.
*/
protected static interface ParameterImplementor<T> extends Parameter<T> {
public boolean isBindable();
public ParameterValue getBoundValue();
}
protected static class ParameterValue {
private final Object value;
private final TemporalType specifiedTemporalType;
public ParameterValue(Object value, TemporalType specifiedTemporalType) {
this.value = value;
this.specifiedTemporalType = specifiedTemporalType;
}
public Object getValue() {
return value;
}
public TemporalType getSpecifiedTemporalType() {
return specifiedTemporalType;
}
}
private Map<ParameterImplementor<?>,ParameterValue> parameterBindingMap;
private Map<ParameterImplementor<?>,ParameterValue> parameterBindingMap() {
if ( parameterBindingMap == null ) {
parameterBindingMap = new HashMap<ParameterImplementor<?>, ParameterValue>();
}
return parameterBindingMap;
}
protected void registerParameter(ParameterImplementor parameter) {
if ( parameter == null ) {
throw new IllegalArgumentException( "parameter cannot be null" );
}
if ( parameterBindingMap().containsKey( parameter ) ) {
return;
}
parameterBindingMap().put( parameter, null );
}
@SuppressWarnings("unchecked")
protected void registerParameterBinding(Parameter parameter, ParameterValue bindValue) {
validateParameterBinding( (ParameterImplementor) parameter, bindValue );
parameterBindingMap().put( (ParameterImplementor) parameter, bindValue );
}
protected void validateParameterBinding(ParameterImplementor parameter, ParameterValue bindValue) {
if ( parameter == null ) {
throw new IllegalArgumentException( "parameter cannot be null" );
}
if ( ! parameter.isBindable() ) {
throw new IllegalArgumentException( "Parameter [" + parameter + "] not valid for binding" );
}
if ( ! parameterBindingMap().containsKey( parameter ) ) {
throw new IllegalArgumentException( "Unknown parameter [" + parameter + "] specified for value binding" );
}
if ( isBound( parameter ) ) {
throw new IllegalArgumentException( "Parameter [" + parameter + "] already had bound value" );
}
validateParameterBindingTypes( parameter, bindValue );
}
protected abstract void validateParameterBindingTypes(ParameterImplementor parameter, ParameterValue bindValue);
protected ParameterValue makeBindValue(Object value) {
return new ParameterValue( value, null );
}
protected ParameterValue makeBindValue(Calendar value, TemporalType temporalType) {
return new ParameterValue( value, temporalType );
}
protected ParameterValue makeBindValue(Date value, TemporalType temporalType) {
return new ParameterValue( value, temporalType );
}
@Override
public <T> BaseQueryImpl setParameter(Parameter<T> param, T value) {
registerParameterBinding( param, makeBindValue( value ) );
return this;
}
@Override
public BaseQueryImpl setParameter(Parameter<Calendar> param, Calendar value, TemporalType temporalType) {
registerParameterBinding( param, makeBindValue( value, temporalType ) );
return this;
}
@Override
public BaseQueryImpl setParameter(Parameter<Date> param, Date value, TemporalType temporalType) {
registerParameterBinding( param, makeBindValue( value, temporalType ) );
return this;
}
@Override
public BaseQueryImpl setParameter(String name, Object value) {
registerParameterBinding( getParameter( name ), makeBindValue( value ) );
return this;
}
@Override
public BaseQueryImpl setParameter(String name, Calendar value, TemporalType temporalType) {
registerParameterBinding( getParameter( name ), makeBindValue( value, temporalType ) );
return this;
}
@Override
public BaseQueryImpl setParameter(String name, Date value, TemporalType temporalType) {
registerParameterBinding( getParameter( name ), makeBindValue( value, temporalType ) );
return this;
}
@Override
public BaseQueryImpl setParameter(int position, Object value) {
registerParameterBinding( getParameter( position ), makeBindValue( value ) );
return this;
}
@Override
public BaseQueryImpl setParameter(int position, Calendar value, TemporalType temporalType) {
registerParameterBinding( getParameter( position ), makeBindValue( value, temporalType ) );
return this;
}
@Override
public BaseQueryImpl setParameter(int position, Date value, TemporalType temporalType) {
registerParameterBinding( getParameter( position ), makeBindValue( value, temporalType ) );
return this;
}
@Override
@SuppressWarnings("unchecked")
public Set<Parameter<?>> getParameters() {
return (Set<Parameter<?>>) parameterBindingMap().keySet();
}
@Override
public Parameter<?> getParameter(String name) {
if ( parameterBindingMap() != null ) {
for ( ParameterImplementor<?> param : parameterBindingMap.keySet() ) {
if ( name.equals( param.getName() ) ) {
return param;
}
}
}
throw new IllegalArgumentException( "Parameter with that name [" + name + "] did not exist" );
}
@Override
@SuppressWarnings("unchecked")
public <T> Parameter<T> getParameter(String name, Class<T> type) {
return (Parameter<T>) getParameter( name );
}
@Override
public Parameter<?> getParameter(int position) {
if ( parameterBindingMap() != null ) {
for ( ParameterImplementor<?> param : parameterBindingMap.keySet() ) {
if ( position == param.getPosition() ) {
return param;
}
}
}
throw new IllegalArgumentException( "Parameter with that position [" + position + "] did not exist" );
}
@Override
@SuppressWarnings("unchecked")
public <T> Parameter<T> getParameter(int position, Class<T> type) {
return (Parameter<T>) getParameter( position );
}
@Override
public boolean isBound(Parameter<?> param) {
return parameterBindingMap() != null
&& parameterBindingMap.get( (ParameterImplementor) param ) != null;
}
@Override
@SuppressWarnings("unchecked")
public <T> T getParameterValue(Parameter<T> param) {
if ( parameterBindingMap != null ) {
final ParameterValue boundValue = parameterBindingMap.get( (ParameterImplementor) param );
if ( boundValue != null ) {
return (T) boundValue.getValue();
}
}
throw new IllegalStateException( "Parameter [" + param + "] has not yet been bound" );
}
@Override
public Object getParameterValue(String name) {
return getParameterValue( getParameter( name ) );
}
@Override
public Object getParameterValue(int position) {
return getParameterValue( getParameter( position ) );
}
}

View File

@ -1,8 +1,10 @@
/*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, 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 Middleware LLC.
* 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
@ -48,7 +50,6 @@ import org.hibernate.Hibernate;
import org.hibernate.SessionFactory;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.ejb.criteria.CriteriaBuilderImpl;
import org.hibernate.ejb.internal.EntityManagerFactoryRegistry;
import org.hibernate.ejb.metamodel.MetamodelImpl;

View File

@ -1,7 +1,7 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009-2011, Red Hat Inc. or third-party contributors as
* Copyright (c) 2009, 2011, 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.
@ -23,24 +23,21 @@
*/
package org.hibernate.ejb;
import java.util.Map;
import javax.persistence.PersistenceContextType;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.spi.PersistenceUnitTransactionType;
import java.util.Map;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.Session;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.spi.SessionOwner;
import org.hibernate.annotations.common.util.ReflectHelper;
import org.hibernate.ejb.internal.EntityManagerMessageLogger;
import org.hibernate.engine.spi.SessionBuilderImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SessionOwner;
/**
* Hibernate implementation of {@link javax.persistence.EntityManager}.
@ -129,26 +126,6 @@ public class EntityManagerImpl extends AbstractEntityManagerImpl implements Sess
return session;
}
@Override
public StoredProcedureQuery createNamedStoredProcedureQuery(String name) {
throw new NotYetImplementedException();
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
throw new NotYetImplementedException();
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) {
throw new NotYetImplementedException();
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) {
throw new NotYetImplementedException();
}
public void close() {
checkEntityManagerFactory();
if ( !open ) {

View File

@ -0,0 +1,283 @@
/*
* 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.ejb;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.Parameter;
import javax.persistence.ParameterMode;
import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.TemporalType;
import java.util.Calendar;
import java.util.Date;
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;
/**
* @author Steve Ebersole
*/
public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredProcedureQuery {
private final StoredProcedureCall storedProcedureCall;
private StoredProcedureOutputs storedProcedureOutputs;
public StoredProcedureQueryImpl(StoredProcedureCall storedProcedureCall, HibernateEntityManagerImplementor entityManager) {
super( entityManager );
this.storedProcedureCall = storedProcedureCall;
}
@Override
protected boolean applyTimeoutHint(int timeout) {
storedProcedureCall.setTimeout( timeout );
return true;
}
@Override
protected boolean applyCacheableHint(boolean isCacheable) {
storedProcedureCall.setCacheable( isCacheable );
return true;
}
@Override
protected boolean applyCacheRegionHint(String regionName) {
storedProcedureCall.setCacheRegion( regionName );
return true;
}
@Override
protected boolean applyReadOnlyHint(boolean isReadOnly) {
storedProcedureCall.setReadOnly( isReadOnly );
return true;
}
@Override
protected boolean applyCacheModeHint(CacheMode cacheMode) {
storedProcedureCall.setCacheMode( cacheMode );
return true;
}
@Override
protected boolean applyFlushModeHint(FlushMode flushMode) {
storedProcedureCall.setFlushMode( flushMode );
return true;
}
@Override
@SuppressWarnings("unchecked")
public StoredProcedureQuery registerStoredProcedureParameter(int position, Class type, ParameterMode mode) {
storedProcedureCall.registerStoredProcedureParameter( position, type, mode );
return this;
}
@Override
public StoredProcedureQuery registerStoredProcedureParameter(String parameterName, Class type, ParameterMode mode) {
storedProcedureCall.registerStoredProcedureParameter( parameterName, type, mode );
return this;
}
@Override
protected void validateParameterBindingTypes(ParameterImplementor parameter, ParameterValue bindValue) {
}
// covariant returns ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public StoredProcedureQueryImpl setFlushMode(FlushModeType jpaFlushMode) {
return (StoredProcedureQueryImpl) super.setFlushMode( jpaFlushMode );
}
@Override
public StoredProcedureQueryImpl setHint(String hintName, Object value) {
return (StoredProcedureQueryImpl) super.setHint( hintName, value );
}
@Override
public <T> StoredProcedureQueryImpl setParameter(Parameter<T> param, T value) {
return (StoredProcedureQueryImpl) super.setParameter( param, value );
}
@Override
public StoredProcedureQueryImpl setParameter(Parameter<Calendar> param, Calendar value, TemporalType temporalType) {
return (StoredProcedureQueryImpl) super.setParameter( param, value, temporalType );
}
@Override
public StoredProcedureQueryImpl setParameter(Parameter<Date> param, Date value, TemporalType temporalType) {
return (StoredProcedureQueryImpl) super.setParameter( param, value, temporalType );
}
@Override
public StoredProcedureQueryImpl setParameter(String name, Object value) {
return ( StoredProcedureQueryImpl) super.setParameter( name, value );
}
@Override
public StoredProcedureQueryImpl setParameter(String name, Calendar value, TemporalType temporalType) {
return (StoredProcedureQueryImpl) super.setParameter( name, value, temporalType );
}
@Override
public StoredProcedureQueryImpl setParameter(String name, Date value, TemporalType temporalType) {
return (StoredProcedureQueryImpl) super.setParameter( name, value, temporalType );
}
@Override
public StoredProcedureQueryImpl setParameter(int position, Object value) {
return (StoredProcedureQueryImpl) super.setParameter( position, value );
}
@Override
public StoredProcedureQueryImpl setParameter(int position, Calendar value, TemporalType temporalType) {
return (StoredProcedureQueryImpl) super.setParameter( position, value, temporalType );
}
@Override
public StoredProcedureQueryImpl setParameter(int position, Date value, TemporalType temporalType) {
return (StoredProcedureQueryImpl) super.setParameter( position, value, temporalType );
}
// outputs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private StoredProcedureOutputs outputs() {
if ( storedProcedureOutputs == null ) {
storedProcedureOutputs = storedProcedureCall.getOutputs();
}
return storedProcedureOutputs;
}
@Override
public Object getOutputParameterValue(int position) {
return outputs().getOutputParameterValue( position );
}
@Override
public Object getOutputParameterValue(String parameterName) {
return outputs().getOutputParameterValue( parameterName );
}
@Override
public boolean execute() {
return outputs().hasMoreReturns();
}
@Override
public int executeUpdate() {
return getUpdateCount();
}
@Override
public boolean hasMoreResults() {
return outputs().hasMoreReturns();
}
@Override
public int getUpdateCount() {
final StoredProcedureReturn nextReturn = outputs().getNextReturn();
if ( nextReturn.isResultSet() ) {
return -1;
}
return ( (StoredProcedureUpdateCountReturn) nextReturn ).getUpdateCount();
}
@Override
public List getResultList() {
final StoredProcedureReturn nextReturn = outputs().getNextReturn();
if ( ! nextReturn.isResultSet() ) {
return null; // todo : what should be thrown/returned here?
}
return ( (StoredProcedureResultSetReturn) nextReturn ).getResultList();
}
@Override
public Object getSingleResult() {
final StoredProcedureReturn nextReturn = outputs().getNextReturn();
if ( ! nextReturn.isResultSet() ) {
return null; // todo : what should be thrown/returned here?
}
return ( (StoredProcedureResultSetReturn) nextReturn ).getSingleResult();
}
@Override
public <T> T unwrap(Class<T> cls) {
return null;
}
// ugh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public Query setLockMode(LockModeType lockMode) {
return null;
}
@Override
public LockModeType getLockMode() {
return null;
}
// unsupported hints/calls ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
protected void applyFirstResult(int firstResult) {
}
@Override
protected void applyMaxResults(int maxResults) {
}
@Override
protected boolean canApplyLockModesHints() {
return false;
}
@Override
protected void applyAliasSpecificLockModeHint(String alias, LockMode lockMode) {
}
@Override
protected boolean applyLockTimeoutHint(int timeout) {
return false;
}
@Override
protected boolean applyCommentHint(String comment) {
return false;
}
@Override
protected boolean applyFetchSize(int fetchSize) {
return false;
}
}