diff --git a/hibernate-core/src/main/java/org/hibernate/BasicQueryContract.java b/hibernate-core/src/main/java/org/hibernate/BasicQueryContract.java
new file mode 100644
index 0000000000..524299c4ac
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/BasicQueryContract.java
@@ -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 in seconds. This value is eventually passed along to the JDBC query via
+ * {@link java.sql.Statement#setQueryTimeout(int)}. Zero indicates no timeout.
+ *
+ * @return The timeout in seconds
+ *
+ * @see java.sql.Statement#getQueryTimeout()
+ * @see java.sql.Statement#setQueryTimeout(int)
+ */
+ public Integer getTimeout();
+
+ /**
+ * Set the query timeout in seconds.
+ *
+ * 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 in seconds
+ *
+ * @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 in seconds
+ *
+ * @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;
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/Query.java b/hibernate-core/src/main/java/org/hibernate/Query.java
index 6b08d63043..c8f202b4f7 100644
--- a/hibernate-core/src/main/java/org/hibernate/Query.java
+++ b/hibernate-core/src/main/java/org/hibernate/Query.java
@@ -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 in seconds. This value is eventually passed along to the JDBC query via
- * {@link java.sql.Statement#setQueryTimeout(int)}. Zero indicates no timeout.
- *
- * @return The timeout in seconds
- *
- * @see java.sql.Statement#getQueryTimeout()
- * @see java.sql.Statement#setQueryTimeout(int)
- */
- public Integer getTimeout();
-
- /**
- * Set the query timeout in seconds.
- *
- * 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 in seconds
- *
- * @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 in seconds
- *
- * @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
diff --git a/hibernate-core/src/main/java/org/hibernate/SQLQuery.java b/hibernate-core/src/main/java/org/hibernate/SQLQuery.java
index 6cc40ae0c8..4938546a27 100755
--- a/hibernate-core/src/main/java/org/hibernate/SQLQuery.java
+++ b/hibernate-core/src/main/java/org/hibernate/SQLQuery.java
@@ -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 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 } element in a
diff --git a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java
index 125ac0563c..b962573da5 100644
--- a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java
+++ b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java
@@ -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);
+
}
diff --git a/hibernate-core/src/main/java/org/hibernate/StoredProcedureCall.java b/hibernate-core/src/main/java/org/hibernate/StoredProcedureCall.java
new file mode 100644
index 0000000000..4d5bfd1e8e
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/StoredProcedureCall.java
@@ -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
+ *
+ * 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 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 {
+ /**
+ * 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 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 {
+ /**
+ * Retrieves the bound value.
+ *
+ * @return The bound value.
+ */
+ public T getValue();
+
+ /**
+ * If {@code } 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();
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/StoredProcedureOutputs.java b/hibernate-core/src/main/java/org/hibernate/StoredProcedureOutputs.java
new file mode 100644
index 0000000000..7591c7d415
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/StoredProcedureOutputs.java
@@ -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();
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/StoredProcedureResultSetReturn.java b/hibernate-core/src/main/java/org/hibernate/StoredProcedureResultSetReturn.java
new file mode 100644
index 0000000000..70b904c26b
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/StoredProcedureResultSetReturn.java
@@ -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();
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/StoredProcedureReturn.java b/hibernate-core/src/main/java/org/hibernate/StoredProcedureReturn.java
new file mode 100644
index 0000000000..d14c4e11ad
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/StoredProcedureReturn.java
@@ -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();
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/StoredProcedureUpdateCountReturn.java b/hibernate-core/src/main/java/org/hibernate/StoredProcedureUpdateCountReturn.java
new file mode 100644
index 0000000000..e4a9324489
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/StoredProcedureUpdateCountReturn.java
@@ -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();
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/SynchronizeableQuery.java b/hibernate-core/src/main/java/org/hibernate/SynchronizeableQuery.java
new file mode 100644
index 0000000000..fa8239dd6a
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/SynchronizeableQuery.java
@@ -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 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;
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractBasicQueryContractImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractBasicQueryContractImpl.java
new file mode 100644
index 0000000000..f6d32d155f
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractBasicQueryContractImpl.java
@@ -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;
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSessionImpl.java
index 82215386e0..7e14ce91ba 100755
--- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSessionImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSessionImpl.java
@@ -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() );
}
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SQLQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SQLQueryImpl.java
index 2f380a8d30..657ab3376b 100644
--- a/hibernate-core/src/main/java/org/hibernate/internal/SQLQueryImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/SQLQueryImpl.java
@@ -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 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( returnAliases.length );
- for ( int i=0; i();
- querySpaces = null;
- callable = false;
+ this.queryReturns = new ArrayList();
+ this.querySpaces = null;
+ this.callable = callable;
}
@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java
index 2a30488f6d..52824ed5e0 100644
--- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java
@@ -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();
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StoredProcedureCallImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StoredProcedureCallImpl.java
new file mode 100644
index 0000000000..ee93509086
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/internal/StoredProcedureCallImpl.java
@@ -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 registeredParameters = new ArrayList();
+
+ private Set synchronizedQuerySpaces;
+
+ @SuppressWarnings("unchecked")
+ public StoredProcedureCallImpl(SessionImplementor session, String procedureName) {
+ this( session, procedureName, (List) null );
+ }
+
+ public StoredProcedureCallImpl(SessionImplementor session, String procedureName, List 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 collectQueryReturns(Class[] resultClasses) {
+ if ( resultClasses == null || resultClasses.length == 0 ) {
+ return null;
+ }
+
+ List queryReturns = new ArrayList( 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 collectQueryReturns(SessionImplementor session, String[] resultSetMappings) {
+ if ( resultSetMappings == null || resultSetMappings.length == 0 ) {
+ return null;
+ }
+
+ List queryReturns = new ArrayList( 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 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 synchronizedQuerySpaces() {
+ if ( synchronizedQuerySpaces == null ) {
+ synchronizedQuerySpaces = new HashSet();
+ }
+ return synchronizedQuerySpaces;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Collection getSynchronizedQuerySpaces() {
+ if ( synchronizedQuerySpaces == null ) {
+ return Collections.emptySet();
+ }
+ else {
+ return Collections.unmodifiableSet( synchronizedQuerySpaces );
+ }
+ }
+
+ public Set getSynchronizedQuerySpacesSet() {
+ return (Set) 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 extends StoredProcedureParameter {
+ public void prepare(CallableStatement statement, int i) throws SQLException;
+
+ public int[] getSqlTypes();
+
+ public T extract(CallableStatement statement);
+ }
+
+ public static abstract class AbstractStoredProcedureParameterImpl implements StoredProcedureParameterImplementor {
+ private final StoredProcedureCallImpl procedureCall;
+
+ private final ParameterMode mode;
+ private final Class type;
+
+ private int startIndex;
+ private Type hibernateType;
+ private int[] sqlTypes;
+
+ private StoredProcedureParameterBindImpl bind;
+
+ protected AbstractStoredProcedureParameterImpl(
+ StoredProcedureCallImpl procedureCall,
+ ParameterMode mode,
+ Class 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 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( 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( 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 implements StoredProcedureParameterBind {
+ 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 extends AbstractStoredProcedureParameterImpl {
+ private final String name;
+
+ public NamedStoredProcedureParameter(
+ StoredProcedureCallImpl procedureCall,
+ String name,
+ ParameterMode mode,
+ Class type) {
+ super( procedureCall, mode, type );
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+ }
+
+ public static class PositionalStoredProcedureParameter extends AbstractStoredProcedureParameterImpl {
+ private final Integer position;
+
+ public PositionalStoredProcedureParameter(
+ StoredProcedureCallImpl procedureCall,
+ Integer position,
+ ParameterMode mode,
+ Class type) {
+ super( procedureCall, mode, type );
+ this.position = position;
+ }
+
+ @Override
+ public Integer getPosition() {
+ return position;
+ }
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StoredProcedureOutputsImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StoredProcedureOutputsImpl.java
new file mode 100644
index 0000000000..f36a7075ad
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/internal/StoredProcedureOutputsImpl.java
@@ -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 customReturns = processor.generateCustomReturns( false );
+
+ CustomQuery customQuery = new CustomQuery() {
+ @Override
+ public String getSQL() {
+ return procedureCall.getProcedureName();
+ }
+
+ @Override
+ public Set getQuerySpaces() {
+ return procedureCall.getSynchronizedQuerySpacesSet();
+ }
+
+ @Override
+ public Map getNamedParameterBindPoints() {
+ // no named parameters in terms of embedded in the SQL string
+ return null;
+ }
+
+ @Override
+ public List 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
+ );
+ }
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java
index 486c62ca57..968d0e899b 100644
--- a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java
+++ b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java
@@ -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.
diff --git a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomQuery.java b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomQuery.java
index be3fe4eab7..ef607040cc 100755
--- a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomQuery.java
+++ b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomQuery.java
@@ -50,7 +50,7 @@ public interface CustomQuery {
*
* @return The query spaces
*/
- public Set getQuerySpaces();
+ public Set 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 getCustomQueryReturns();
}
diff --git a/hibernate-core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java b/hibernate-core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java
index eb3c85323e..fe2cc4713d 100644
--- a/hibernate-core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java
+++ b/hibernate-core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java
@@ -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 generateCustomReturns(boolean queryHadAliases) {
+ List customReturns = new ArrayList();
+ Map customReturnsByAlias = new HashMap();
+ 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();
diff --git a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java
index 6f784a9020..f2c452c70c 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java
@@ -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
- implements BasicType, StringRepresentableType, XmlRepresentableType {
+ implements BasicType, StringRepresentableType, XmlRepresentableType, ProcedureParameterExtractionAware {
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
? 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 );
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java
index 74310ca0b7..9c69f10b61 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java
@@ -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 );
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/PostgresUUIDType.java b/hibernate-core/src/main/java/org/hibernate/type/PostgresUUIDType.java
index 3b2e3cdbf7..e199d8881a 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/PostgresUUIDType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/PostgresUUIDType.java
@@ -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 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;
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/ValueExtractor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/ValueExtractor.java
index 0194cad357..4e99135566 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/ValueExtractor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/ValueExtractor.java
@@ -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 {
* @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;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BasicExtractor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BasicExtractor.java
index 72643cc4dc..792f39d2f8 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BasicExtractor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BasicExtractor.java
@@ -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 implements ValueExtractor {
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 implements ValueExtractor {
* @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.
+ *
+ * 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;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BigIntTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BigIntTypeDescriptor.java
index e861fc0b1b..cf2103d90e 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BigIntTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BigIntTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BitTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BitTypeDescriptor.java
index a632106326..a60fa08d4a 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BitTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BitTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BlobTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BlobTypeDescriptor.java
index 9b61c15469..1cda8b54a1 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BlobTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BlobTypeDescriptor.java
@@ -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 );
+ }
};
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BooleanTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BooleanTypeDescriptor.java
index a4ae309bf4..afa54c280f 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BooleanTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/BooleanTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/ClobTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/ClobTypeDescriptor.java
index 5d9fe02696..696df0e78e 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/ClobTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/ClobTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DateTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DateTypeDescriptor.java
index 4b6dcf6668..4bb7a702e2 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DateTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DateTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DecimalTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DecimalTypeDescriptor.java
index 0431047e87..397c96f269 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DecimalTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DecimalTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DoubleTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DoubleTypeDescriptor.java
index 8006dabc66..13b6a1a045 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DoubleTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/DoubleTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/IntegerTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/IntegerTypeDescriptor.java
index 8676cfbc16..8834e2d598 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/IntegerTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/IntegerTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/RealTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/RealTypeDescriptor.java
index f57fcdcdb8..85c2a15559 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/RealTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/RealTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/SmallIntTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/SmallIntTypeDescriptor.java
index b980eca205..0786ea242c 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/SmallIntTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/SmallIntTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimeTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimeTypeDescriptor.java
index 8915930f07..2b1b1b034c 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimeTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimeTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimestampTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimestampTypeDescriptor.java
index 28c903c6b5..4401becfc8 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimestampTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TimestampTypeDescriptor.java
@@ -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 );
+ }
};
}
}
\ No newline at end of file
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TinyIntTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TinyIntTypeDescriptor.java
index 0fa283a8aa..8418a28526 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TinyIntTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/TinyIntTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/VarbinaryTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/VarbinaryTypeDescriptor.java
index 4f75a4aa84..7c7d28a807 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/VarbinaryTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/VarbinaryTypeDescriptor.java
@@ -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( 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 );
}
};
}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/VarcharTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/VarcharTypeDescriptor.java
index c5300fca50..50d5f3c5a9 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/VarcharTypeDescriptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/VarcharTypeDescriptor.java
@@ -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 );
+ }
};
}
}
diff --git a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java
new file mode 100644
index 0000000000..f4e54c800b
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java
@@ -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();
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/test/typeoverride/StoredPrefixedStringType.java b/hibernate-core/src/test/java/org/hibernate/test/typeoverride/StoredPrefixedStringType.java
index ee32bb28a7..322a777a5b 100644
--- a/hibernate-core/src/test/java/org/hibernate/test/typeoverride/StoredPrefixedStringType.java
+++ b/hibernate-core/src/test/java/org/hibernate/test/typeoverride/StoredPrefixedStringType.java
@@ -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 );
+ }
};
}
};
diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
index 0925c26268..368ff7b9f3 100755
--- a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
+++ b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
@@ -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 getReference(Class entityClass, Object primaryKey) {
try {
diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/BaseQueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/BaseQueryImpl.java
new file mode 100644
index 0000000000..d0aed68efd
--- /dev/null
+++ b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/BaseQueryImpl.java
@@ -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 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 getSupportedHints() {
+ return QueryHints.getDefinedHints();
+ }
+
+ @Override
+ public Map 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();
+ }
+ 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 parameters;
+
+ /**
+ * Hibernate specific extension to the JPA {@link Parameter} contract.
+ */
+ protected static interface ParameterImplementor extends Parameter {
+ 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,ParameterValue> parameterBindingMap;
+
+ private Map,ParameterValue> parameterBindingMap() {
+ if ( parameterBindingMap == null ) {
+ parameterBindingMap = new HashMap, 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 BaseQueryImpl setParameter(Parameter param, T value) {
+ registerParameterBinding( param, makeBindValue( value ) );
+ return this;
+ }
+
+ @Override
+ public BaseQueryImpl setParameter(Parameter param, Calendar value, TemporalType temporalType) {
+ registerParameterBinding( param, makeBindValue( value, temporalType ) );
+ return this;
+ }
+
+ @Override
+ public BaseQueryImpl setParameter(Parameter 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> getParameters() {
+ return (Set>) 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 Parameter getParameter(String name, Class type) {
+ return (Parameter) 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 Parameter getParameter(int position, Class type) {
+ return (Parameter) getParameter( position );
+ }
+
+ @Override
+ public boolean isBound(Parameter> param) {
+ return parameterBindingMap() != null
+ && parameterBindingMap.get( (ParameterImplementor) param ) != null;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T getParameterValue(Parameter 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 ) );
+ }
+}
diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java
index 86e5573e6f..0525efa579 100755
--- a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java
+++ b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/EntityManagerFactoryImpl.java
@@ -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;
diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/EntityManagerImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/EntityManagerImpl.java
index d34d476d6b..05ba76dd08 100755
--- a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/EntityManagerImpl.java
+++ b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/EntityManagerImpl.java
@@ -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 ) {
diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/StoredProcedureQueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/StoredProcedureQueryImpl.java
new file mode 100644
index 0000000000..c1c3e4f96b
--- /dev/null
+++ b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/StoredProcedureQueryImpl.java
@@ -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 StoredProcedureQueryImpl setParameter(Parameter param, T value) {
+ return (StoredProcedureQueryImpl) super.setParameter( param, value );
+ }
+
+ @Override
+ public StoredProcedureQueryImpl setParameter(Parameter param, Calendar value, TemporalType temporalType) {
+ return (StoredProcedureQueryImpl) super.setParameter( param, value, temporalType );
+ }
+
+ @Override
+ public StoredProcedureQueryImpl setParameter(Parameter 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 unwrap(Class 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;
+ }
+}