diff --git a/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java b/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java index f2ea5e2ab6..f395af810a 100644 --- a/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java +++ b/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java @@ -116,6 +116,10 @@ public abstract class AbstractQueryImpl implements Query { this.parameterMetadata = parameterMetadata; } + public ParameterMetadata getParameterMetadata() { + return parameterMetadata; + } + public String toString() { return StringHelper.unqualify( getClass().getName() ) + '(' + queryString + ')'; } @@ -161,7 +165,13 @@ public abstract class AbstractQueryImpl implements Query { } public Query setMaxResults(int maxResults) { - selection.setMaxRows( new Integer(maxResults) ); + if ( maxResults < 0 ) { + // treat negatives specically as meaning no limit... + selection.setMaxRows( null ); + } + else { + selection.setMaxRows( new Integer(maxResults) ); + } return this; } diff --git a/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java b/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java index 2a44ab3d6e..d04e898234 100755 --- a/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC 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. + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by + * third-party contributors as indicated by either @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 @@ -80,6 +82,7 @@ import org.hibernate.util.JTAHelper; /** * @author Gavin King * @author Emmanuel Bernard + * @author Steve Ebersole */ @SuppressWarnings("unchecked") public abstract class AbstractEntityManagerImpl implements HibernateEntityManagerImplementor, Serializable { @@ -115,19 +118,34 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage this.properties = null; } - public Query createQuery(String ejbqlString) { - //adjustFlushMode(); + public Query createQuery(String jpaqlString) { try { - return new QueryImpl( getSession().createQuery( ejbqlString ), this ); + return new QueryImpl( getSession().createQuery( jpaqlString ), this ); } catch ( HibernateException he ) { - throwPersistenceException( he ); - return null; + throw convert( he ); } } - public TypedQuery createQuery(String qlString, Class resultClass) { - throw new UnsupportedOperationException( "Not yet implemented" ); + public TypedQuery createQuery(String jpaqlString, Class resultClass) { + try { + org.hibernate.Query hqlQuery = getSession().createQuery( jpaqlString ); + if ( hqlQuery.getReturnTypes().length != 1 ) { + throw new IllegalArgumentException( "Cannot create TypedQuery for query with more than one return" ); + } + if ( ! resultClass.isAssignableFrom( hqlQuery.getReturnTypes()[0].getReturnedClass() ) ) { + throw new IllegalArgumentException( + "Type specified for TypedQuery [" + + resultClass.getName() + + "] is incompatible with query return type [" + + hqlQuery.getReturnTypes()[0].getReturnedClass() + "]" + ); + } + return new QueryImpl( hqlQuery, this ); + } + catch ( HibernateException he ) { + throw convert( he ); + } } public TypedQuery createQuery(CriteriaQuery criteriaQuery) { @@ -165,69 +183,81 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage } public Query createNamedQuery(String name) { - //adjustFlushMode(); - org.hibernate.Query namedQuery; try { - namedQuery = getSession().getNamedQuery( name ); + org.hibernate.Query namedQuery = getSession().getNamedQuery( name ); + try { + return new QueryImpl( namedQuery, this ); + } + catch ( HibernateException he ) { + throw convert( he ); + } } catch ( MappingException e ) { throw new IllegalArgumentException( "Named query not found: " + name ); } - try { - return new QueryImpl( namedQuery, this ); - } - catch ( HibernateException he ) { - throwPersistenceException( he ); - return null; - } } public TypedQuery createNamedQuery(String name, Class resultClass) { - throw new UnsupportedOperationException( "Not yet implemented" ); + try { + org.hibernate.Query namedQuery = getSession().getNamedQuery( name ); + try { + if ( namedQuery.getReturnTypes().length != 1 ) { + throw new IllegalArgumentException( "Cannot create TypedQuery for query with more than one return" ); + } + if ( ! resultClass.isAssignableFrom( namedQuery.getReturnTypes()[0].getReturnedClass() ) ) { + throw new IllegalArgumentException( + "Type specified for TypedQuery [" + + resultClass.getName() + + "] is incompatible with query return type [" + + namedQuery.getReturnTypes()[0].getReturnedClass() + "]" + ); + } + return new QueryImpl( namedQuery, this ); + } + catch ( HibernateException he ) { + throw convert( he ); + } + } + catch ( MappingException e ) { + throw new IllegalArgumentException( "Named query not found: " + name ); + } } public Query createNativeQuery(String sqlString) { - //adjustFlushMode(); try { SQLQuery q = getSession().createSQLQuery( sqlString ); return new QueryImpl( q, this ); } catch ( HibernateException he ) { - throwPersistenceException( he ); - return null; + throw convert( he ); } } public Query createNativeQuery(String sqlString, Class resultClass) { - //adjustFlushMode(); try { SQLQuery q = getSession().createSQLQuery( sqlString ); q.addEntity( "alias1", resultClass.getName(), LockMode.READ ); return new QueryImpl( q, this ); } catch ( HibernateException he ) { - throwPersistenceException( he ); - return null; + throw convert( he ); } } public Query createNativeQuery(String sqlString, String resultSetMapping) { - //adjustFlushMode(); try { SQLQuery q = getSession().createSQLQuery( sqlString ); q.setResultSetMapping( resultSetMapping ); return new QueryImpl( q, this ); } catch ( HibernateException he ) { - throwPersistenceException( he ); - return null; + throw convert( he ); } } @SuppressWarnings("unchecked") public T getReference(Class entityClass, Object primaryKey) { - //adjustFlushMode(); try { return ( T ) getSession().load( entityClass, ( Serializable ) primaryKey ); } @@ -241,14 +271,12 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage throw new IllegalArgumentException( e.getMessage(), e ); } catch ( HibernateException he ) { - throwPersistenceException( he ); - return null; + throw convert( he ); } } @SuppressWarnings("unchecked") public A find(Class entityClass, Object primaryKey) { - //adjustFlushMode(); try { return ( A ) getSession().get( entityClass, ( Serializable ) primaryKey ); } @@ -270,8 +298,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage throw new IllegalArgumentException( e.getMessage(), e ); } catch ( HibernateException he ) { - throwPersistenceException( he ); - return null; + throw convert( he ); } } @@ -301,7 +328,6 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage public void persist(Object entity) { checkTransactionNeeded(); - //adjustFlushMode(); try { getSession().persist( entity ); } @@ -309,14 +335,13 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage throw new IllegalArgumentException( e.getMessage() ); } catch ( HibernateException he ) { - throwPersistenceException( he ); + throw convert( he ); } } @SuppressWarnings("unchecked") public A merge(A entity) { checkTransactionNeeded(); - //adjustFlushMode(); try { return ( A ) getSession().merge( entity ); } @@ -327,14 +352,12 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage throw new IllegalArgumentException( e.getMessage(), e ); } catch ( HibernateException he ) { - throwPersistenceException( he ); - return null; + throw convert( he ); } } public void remove(Object entity) { checkTransactionNeeded(); - //adjustFlushMode(); try { getSession().delete( entity ); } @@ -342,13 +365,12 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage throw new IllegalArgumentException( e.getMessage(), e ); } catch ( HibernateException he ) { - throwPersistenceException( he ); + throw convert( he ); } } public void refresh(Object entity) { checkTransactionNeeded(); - //adjustFlushMode(); try { if ( !getSession().contains( entity ) ) { throw new IllegalArgumentException( "Entity not managed" ); @@ -359,7 +381,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage throw new IllegalArgumentException( e.getMessage(), e ); } catch ( HibernateException he ) { - throwPersistenceException( he ); + throw convert( he ); } } @@ -391,8 +413,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage throw new IllegalArgumentException( e.getMessage(), e ); } catch ( HibernateException he ) { - throwPersistenceException( he ); - return false; + throw convert( he ); } } @@ -425,7 +446,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage getSession().flush(); } catch ( HibernateException he ) { - throwPersistenceException( he ); + throw convert( he ); } } @@ -490,7 +511,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage getSession().clear(); } catch ( HibernateException he ) { - throwPersistenceException( he ); + throw convert( he ); } } @@ -499,7 +520,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage getSession().evict( entity ); } catch ( HibernateException he ) { - throwPersistenceException( he ); + throw convert( he ); } } @@ -672,14 +693,17 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage log.trace( "skipping managed flushing" ); } } + catch ( HibernateException he ) { + throw convert( he ); + } + catch( PersistenceException pe ) { + handlePersistenceException( pe ); + throw pe; + } catch ( RuntimeException re ) { - //throwPersistenceException will mark the transaction as rollbacked - if ( re instanceof HibernateException ) { - throwPersistenceException( ( HibernateException ) re ); - } - else { - throwPersistenceException( new PersistenceException( re ) ); - } + PersistenceException wrapped = new PersistenceException( re ); + handlePersistenceException( wrapped ); + throw wrapped; } } @@ -698,7 +722,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage } } catch ( HibernateException e ) { - throwPersistenceException( e ); + throw convert( e ); } } } @@ -709,7 +733,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage } } catch ( HibernateException he ) { - throwPersistenceException( he ); + throw convert( he ); } } else { @@ -735,35 +759,60 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage tx = new TransactionImpl( this ); } - public void throwPersistenceException(PersistenceException e) { - if ( !( e instanceof NoResultException || e instanceof NonUniqueResultException ) ) { - try { - markAsRollback(); - } - catch ( Exception ne ) { - //we do not want the subsequent exception to swallow the original one - log.error( "Unable to mark for rollback on PersistenceException: ", ne ); - } + /** + * {@inheritDoc} + */ + public void handlePersistenceException(PersistenceException e) { + if ( e instanceof NoResultException ) { + return; } + if ( e instanceof NonUniqueResultException ) { + return; + } + + try { + markAsRollback(); + } + catch ( Exception ne ) { + //we do not want the subsequent exception to swallow the original one + log.error( "Unable to mark for rollback on PersistenceException: ", ne ); + } + } + + /** + * {@inheritDoc} + */ + public void throwPersistenceException(PersistenceException e) { + handlePersistenceException( e ); throw e; } - public void throwPersistenceException(HibernateException e) { + /** + * {@inheritDoc} + */ + public RuntimeException convert(HibernateException e) { if ( e instanceof StaleStateException ) { - PersistenceException pe = wrapStaleStateException( ( StaleStateException ) e ); - throwPersistenceException( pe ); + PersistenceException converted = wrapStaleStateException( ( StaleStateException ) e ); + handlePersistenceException( converted ); + return converted; } else if ( e instanceof ObjectNotFoundException ) { - throwPersistenceException( new EntityNotFoundException( e.getMessage() ) ); + EntityNotFoundException converted = new EntityNotFoundException( e.getMessage() ); + handlePersistenceException( converted ); + return converted; } else if ( e instanceof org.hibernate.NonUniqueResultException ) { - throwPersistenceException( new NonUniqueResultException( e.getMessage() ) ); + NonUniqueResultException converted = new NonUniqueResultException( e.getMessage() ); + handlePersistenceException( converted ); + return converted; } else if ( e instanceof UnresolvableObjectException ) { - throwPersistenceException( new EntityNotFoundException( e.getMessage() ) ); + EntityNotFoundException converted = new EntityNotFoundException( e.getMessage() ); + handlePersistenceException( converted ); + return converted; } else if ( e instanceof QueryException ) { - throw new IllegalArgumentException( e ); + return new IllegalArgumentException( e ); } else if ( e instanceof TransientObjectException ) { try { @@ -773,13 +822,25 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage //we do not want the subsequent exception to swallow the original one log.error( "Unable to mark for rollback on TransientObjectException: ", ne ); } - throw new IllegalStateException( e ); //Spec 3.2.3 Synchronization rules + return new IllegalStateException( e ); //Spec 3.2.3 Synchronization rules } else { - throwPersistenceException( new PersistenceException( e ) ); + PersistenceException converted = new PersistenceException( e ); + handlePersistenceException( converted ); + return converted; } } + /** + * {@inheritDoc} + */ + public void throwPersistenceException(HibernateException e) { + throw convert( e ); + } + + /** + * {@inheritDoc} + */ public PersistenceException wrapStaleStateException(StaleStateException e) { PersistenceException pe; if ( e instanceof StaleObjectStateException ) { diff --git a/entitymanager/src/main/java/org/hibernate/ejb/AbstractQueryImpl.java b/entitymanager/src/main/java/org/hibernate/ejb/AbstractQueryImpl.java new file mode 100644 index 0000000000..27d1a28805 --- /dev/null +++ b/entitymanager/src/main/java/org/hibernate/ejb/AbstractQueryImpl.java @@ -0,0 +1,352 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by + * third-party contributors as indicated by either @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 java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.persistence.FlushModeType; +import javax.persistence.Parameter; +import javax.persistence.TransactionRequiredException; +import javax.persistence.TypedQuery; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.CacheMode; +import org.hibernate.FlushMode; +import org.hibernate.HibernateException; +import org.hibernate.TypeMismatchException; +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 org.hibernate.ejb.util.ConfigurationHelper; +import org.hibernate.hql.QueryExecutionRequestException; + +/** + * Intended as a base class providing convenience in implementing both {@link javax.persistence.Query} and + * {@link javax.persistence.TypedQuery}. + *

+ * IMPL NOTE : This issue, and the reason for this distinction, is that criteria and hl.sql queries share no + * commonality currently in Hibernate internals. + * + * @author Steve Ebersole + */ +public abstract class AbstractQueryImpl implements TypedQuery { + private static final Logger log = LoggerFactory.getLogger( AbstractQueryImpl.class ); + + private final HibernateEntityManagerImplementor entityManager; + + public AbstractQueryImpl(HibernateEntityManagerImplementor entityManager) { + this.entityManager = entityManager; + } + + protected HibernateEntityManagerImplementor getEntityManager() { + return entityManager; + } + + /** + * Actually execute the update; all pre-requisites have been checked. + * + * @return The number of "affected rows". + */ + protected abstract int internalExecuteUpdate(); + + /** + * {@inheritDoc} + */ + @SuppressWarnings({ "ThrowableInstanceNeverThrown" }) + public int executeUpdate() { + try { + if ( ! entityManager.isTransactionInProgress() ) { + entityManager.throwPersistenceException( new TransactionRequiredException( "Executing an update/delete query" ) ); + return 0; + } + return internalExecuteUpdate(); + } + catch ( QueryExecutionRequestException he) { + throw new IllegalStateException(he); + } + catch( TypeMismatchException e ) { + throw new IllegalArgumentException(e); + } + catch ( HibernateException he) { + entityManager.throwPersistenceException( he ); + return 0; + } + } + + private int maxResults = -1; + + /** + * Apply the given max results value. + * + * @param maxResults The specified max results + */ + protected abstract void applyMaxResults(int maxResults); + + /** + * {@inheritDoc} + */ + public TypedQuery 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; + } + + public int getMaxResults() { + return maxResults == -1 + ? Integer.MAX_VALUE // stupid spec... MAX_VALUE?? + : maxResults; + } + + private int firstResult; + + /** + * Apply the given first-result value. + * + * @param firstResult The specified first-result value. + */ + protected abstract void applyFirstResult(int firstResult); + + public TypedQuery setFirstResult(int firstResult) { + if ( firstResult < 0 ) { + throw new IllegalArgumentException( + "Negative value (" + firstResult + ") passed to setFirstResult" + ); + } + this.firstResult = firstResult; + applyFirstResult( firstResult ); + return this; + } + + public int getFirstResult() { + return firstResult; + } + + private Map hints; + + /** + * {@inheritDoc} + */ + public Map getHints() { + return hints; + } + + protected abstract void applyTimeout(int timeout); + + protected abstract void applyComment(String comment); + + protected abstract void applyFetchSize(int fetchSize); + + protected abstract void applyCacheable(boolean isCacheable); + + protected abstract void applyCacheRegion(String regionName); + + protected abstract void applyReadOnly(boolean isReadOnly); + + protected abstract void applyCacheMode(CacheMode cacheMode); + + protected abstract void applyFlushMode(FlushMode flushMode); + + /** + * {@inheritDoc} + */ + public TypedQuery setHint(String hintName, Object value) { + boolean skipped = false; + try { + if ( HINT_TIMEOUT.equals( hintName ) ) { + applyTimeout( ConfigurationHelper.getInteger( value ) ); + } + else if ( HINT_COMMENT.equals( hintName ) ) { + applyComment( (String) value ); + } + else if ( HINT_FETCH_SIZE.equals( hintName ) ) { + applyFetchSize( ConfigurationHelper.getInteger( value ) ); + } + else if ( HINT_CACHEABLE.equals( hintName ) ) { + applyCacheable( ConfigurationHelper.getBoolean( value ) ); + } + else if ( HINT_CACHE_REGION.equals( hintName ) ) { + applyCacheRegion( (String) value ); + } + else if ( HINT_READONLY.equals( hintName ) ) { + applyReadOnly( ConfigurationHelper.getBoolean( value ) ); + } + else if ( HINT_CACHE_MODE.equals( hintName ) ) { + applyCacheMode( ConfigurationHelper.getCacheMode( value ) ); + } + else if ( HINT_FLUSH_MODE.equals( hintName ) ) { + applyFlushMode( ConfigurationHelper.getFlushMode( value ) ); + } + /* TODO: + else if ( "org.hibernate.lockMode".equals( hintName ) ) { + query.setLockMode( alias, lockMode ); + }*/ + else { + skipped = true; + log.info( "Ignoring unrecognized query hint [" + hintName + "]" ); + } + } + catch ( ClassCastException e ) { + throw new IllegalArgumentException( "Value for hint" ); + } + + if ( !skipped ) { + if ( hints == null ) { + hints = new HashMap(); + } + hints.put( hintName, value ); + } + + return this; + } + + public Set getSupportedHints() { + return QueryHints.getDefinedHints(); + } + + private FlushModeType jpaFlushMode; + + public TypedQuery setFlushMode(FlushModeType jpaFlushMode) { + this.jpaFlushMode = jpaFlushMode; + // TODO : treat as hint? + if ( jpaFlushMode == FlushModeType.AUTO ) { + applyFlushMode( FlushMode.AUTO ); + } + else if ( jpaFlushMode == FlushModeType.COMMIT ) { + applyFlushMode( FlushMode.COMMIT ); + } + return this; + } + + protected FlushModeType getSpecifiedFlushMode() { + return jpaFlushMode; + } + + public FlushModeType getFlushMode() { + return jpaFlushMode != null + ? jpaFlushMode + : entityManager.getFlushMode(); + } + + private Map parameterBindings; + + protected void registerParameterBinding(Parameter parameter, Object value) { + if ( value != null && parameter.getJavaType() != null ) { + if ( Collection.class.isInstance( value ) ) { + final Collection collection = (Collection) value; + // validate the elements... + for ( Object element : collection ) { + if ( ! parameter.getJavaType().isInstance( element ) ) { + throw new IllegalArgumentException( + "Parameter value [" + element + "] was not matching type [" + + parameter.getJavaType().getName() + "]" + ); + } + } + } + else if ( value.getClass().isArray() ) { + final Object[] array = (Object[]) value; + for ( Object element : array ) { + if ( ! parameter.getJavaType().isInstance( element ) ) { + throw new IllegalArgumentException( + "Parameter value [" + element + "] was not matching type [" + + parameter.getJavaType().getName() + "]" + ); + } + } + } + else { + if ( ! parameter.getJavaType().isInstance( value ) ) { + throw new IllegalArgumentException( + "Parameter value [" + value + "] was not matching type [" + + parameter.getJavaType().getName() + "]" + ); + } + } + } + + if ( parameterBindings == null ) { + parameterBindings = new HashMap(); + } + parameterBindings.put( parameter, value ); + } + + /** + * {@inheritDoc} + */ + public boolean isBound(Parameter param) { + return parameterBindings != null && parameterBindings.containsKey( param ); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings({ "unchecked" }) + public T getParameterValue(Parameter param) { + if ( parameterBindings == null ) { + throw new IllegalStateException( "No parameters have been bound" ); + } + try { + T value = (T) parameterBindings.get( param ); + if ( value == null ) { + throw new IllegalStateException( "Parameter has not been bound" ); + } + return value; + } + catch ( ClassCastException cce ) { + throw new IllegalStateException( "Encountered a parameter value type exception" ); + } + } + + /** + * {@inheritDoc} + */ + public Object getParameterValue(String name) { + return getParameterValue( getParameter( name ) ); + } + + /** + * {@inheritDoc} + */ + public Object getParameterValue(int position) { + return getParameterValue( getParameter( position ) ); + } +} diff --git a/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java b/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java index 6e0d56b618..850e7d48e7 100644 --- a/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC 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. + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by + * third-party contributors as indicated by either @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 @@ -19,8 +21,6 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ - -//$Id$ package org.hibernate.ejb; import javax.persistence.PersistenceException; @@ -29,13 +29,50 @@ import org.hibernate.HibernateException; import org.hibernate.StaleStateException; /** + * Additional internal contracts for the Hibernate {@link javax.persistence.EntityManager} implementation. + * * @author Emmanuel Bernard + * @author Steve Ebersole */ public interface HibernateEntityManagerImplementor extends HibernateEntityManager { + /** + * Provides access to whether a transaction is currently in progress. + * + * @return True if a transaction is considered currently in progress; false otherwise. + */ boolean isTransactionInProgress(); + /** + * Handles marking for rollback and other such operations that need to occur depending on the type of + * exception being handled. + * + * @param e The exception being handled. + */ + public void handlePersistenceException(PersistenceException e); + + /** + * Delegates to {@link #handlePersistenceException} and then throws the given exception. + * + * @param e The exception being handled and finally thrown. + */ public void throwPersistenceException(PersistenceException e); + /** + * Converts a Hibernate-specific exception into a JPA-specified exception; note that the JPA sepcification makes use + * of exceptions outside its exception hierarchy, though they are all runtime exceptions. + *

+ * Any appropriate/needed calls to {@link #handlePersistenceException} are also made. + * + * @param e The Hibernate excepton. + * @return The JPA-specified exception + */ + public RuntimeException convert(HibernateException e); + + /** + * Delegates to {@link #convert} and then throws the given exception. + * + * @param e The exception being handled and finally thrown. + */ public void throwPersistenceException(HibernateException e); public PersistenceException wrapStaleStateException(StaleStateException e); diff --git a/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java b/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java index 36fdd45983..e2b7dd4680 100755 --- a/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC 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. + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by + * third-party contributors as indicated by either @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 @@ -17,31 +19,16 @@ * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA + * Boston, MA 02110-1301 USA\ */ - -//$Id$ package org.hibernate.ejb; import java.util.Map; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Modifier; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; import javax.persistence.EntityManagerFactory; -import javax.persistence.PersistenceException; -import javax.persistence.spi.PersistenceUnitInfo; import javax.persistence.spi.LoadState; +import javax.persistence.spi.PersistenceUnitInfo; -import org.hibernate.Hibernate; import org.hibernate.ejb.util.PersistenceUtilHelper; -import org.hibernate.intercept.FieldInterceptionHelper; -import org.hibernate.intercept.FieldInterceptor; -import org.hibernate.collection.PersistentCollection; -import org.hibernate.proxy.HibernateProxy; -import org.hibernate.proxy.LazyInitializer; /** * Hibernate EJB3 persistence provider implementation diff --git a/entitymanager/src/main/java/org/hibernate/ejb/HibernateQuery.java b/entitymanager/src/main/java/org/hibernate/ejb/HibernateQuery.java index cd7a40584f..499c4b3983 100755 --- a/entitymanager/src/main/java/org/hibernate/ejb/HibernateQuery.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/HibernateQuery.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC 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. + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by + * third-party contributors as indicated by either @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 @@ -17,10 +19,8 @@ * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA + * Boston, MA 02110-1301 USA\ */ - -//$Id$ package org.hibernate.ejb; import javax.persistence.Query; diff --git a/entitymanager/src/main/java/org/hibernate/ejb/QueryHints.java b/entitymanager/src/main/java/org/hibernate/ejb/QueryHints.java new file mode 100644 index 0000000000..2ef5e8c562 --- /dev/null +++ b/entitymanager/src/main/java/org/hibernate/ejb/QueryHints.java @@ -0,0 +1,62 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by + * third-party contributors as indicated by either @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 java.util.Set; +import java.util.HashSet; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +public class QueryHints { + public static final String HINT_TIMEOUT = "org.hibernate.timeout"; + public static final String HINT_COMMENT = "org.hibernate.comment"; + public static final String HINT_FETCH_SIZE = "org.hibernate.fetchSize"; + public static final String HINT_CACHE_REGION = "org.hibernate.cacheRegion"; + public static final String HINT_CACHEABLE = "org.hibernate.cacheable"; + public static final String HINT_READONLY = "org.hibernate.readOnly"; + public static final String HINT_CACHE_MODE = "org.hibernate.cacheMode"; + public static final String HINT_FLUSH_MODE = "org.hibernate.flushMode"; + + private static final Set HINTS = buildHintsSet(); + + private static Set buildHintsSet() { + HashSet hints = new HashSet(); + hints.add( HINT_TIMEOUT ); + hints.add( HINT_COMMENT ); + hints.add( HINT_FETCH_SIZE ); + hints.add( HINT_CACHE_REGION ); + hints.add( HINT_CACHEABLE ); + hints.add( HINT_READONLY ); + hints.add( HINT_CACHE_MODE ); + hints.add( HINT_FLUSH_MODE ); + return java.util.Collections.unmodifiableSet( hints ); + } + + public static Set getDefinedHints() { + return HINTS; + } +} diff --git a/entitymanager/src/main/java/org/hibernate/ejb/QueryImpl.java b/entitymanager/src/main/java/org/hibernate/ejb/QueryImpl.java index d09fdaa317..c09552bb07 100755 --- a/entitymanager/src/main/java/org/hibernate/ejb/QueryImpl.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/QueryImpl.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC 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. + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by + * third-party contributors as indicated by either @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 @@ -26,55 +28,187 @@ package org.hibernate.ejb; import java.util.Calendar; import java.util.Collection; import java.util.Date; -import java.util.List; import java.util.HashSet; +import java.util.List; import java.util.Set; -import java.util.Map; -import javax.persistence.FlushModeType; +import javax.persistence.LockModeType; import javax.persistence.NoResultException; import javax.persistence.NonUniqueResultException; +import javax.persistence.Parameter; import javax.persistence.Query; import javax.persistence.TemporalType; -import static javax.persistence.TemporalType.*; -import javax.persistence.TransactionRequiredException; -import javax.persistence.LockModeType; -import javax.persistence.Parameter; +import static javax.persistence.TemporalType.DATE; +import static javax.persistence.TemporalType.TIME; +import static javax.persistence.TemporalType.TIMESTAMP; +import javax.persistence.TypedQuery; +import javax.persistence.PersistenceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.QueryParameterException; import org.hibernate.TypeMismatchException; -import org.hibernate.impl.AbstractQueryImpl; -import org.hibernate.ejb.util.ConfigurationHelper; +import org.hibernate.engine.query.NamedParameterDescriptor; +import org.hibernate.engine.query.OrdinalParameterDescriptor; import org.hibernate.hql.QueryExecutionRequestException; +import org.hibernate.impl.AbstractQueryImpl; /** + * Hibernate implementation of both the {@link Query} and {@link TypedQuery} contracts. + * * @author Gavin King * @author Emmanuel Bernard + * @author Steve Ebersole */ -public class QueryImpl implements Query, HibernateQuery { +public class QueryImpl extends org.hibernate.ejb.AbstractQueryImpl implements TypedQuery, HibernateQuery { + private static final Logger log = LoggerFactory.getLogger( QueryImpl.class ); + private org.hibernate.Query query; - private HibernateEntityManagerImplementor em; - private Boolean isPositional = null; - private int maxResults = -1; - private int firstResult; + private Set jpaPositionalIndices; + private Set> parameters; public QueryImpl(org.hibernate.Query query, AbstractEntityManagerImpl em) { + super( em ); this.query = query; - this.em = em; + extractParameterInfo(); + } + + @SuppressWarnings({ "unchecked" }) + private void extractParameterInfo() { + if ( ! AbstractQueryImpl.class.isInstance( query ) ) { + throw new IllegalStateException( "Unknown query type for parameter extraction" ); + } + + HashSet> parameters = new HashSet>(); + AbstractQueryImpl queryImpl = AbstractQueryImpl.class.cast( query ); + + // extract named params + for ( String name : (Set) queryImpl.getParameterMetadata().getNamedParameterNames() ) { + final NamedParameterDescriptor descriptor = + queryImpl.getParameterMetadata().getNamedParameterDescriptor( name ); + final ParameterImpl parameter = new ParameterImpl( + name, + descriptor.getExpectedType() == null + ? null + : descriptor.getExpectedType().getReturnedClass() + ); + parameters.add( parameter ); + if ( descriptor.isJpaStyle() ) { + if ( jpaPositionalIndices == null ) { + jpaPositionalIndices = new HashSet(); + } + jpaPositionalIndices.add( Integer.valueOf( name ) ); + } + } + + // extract positional parameters + for ( int i = 0, max = queryImpl.getParameterMetadata().getOrdinalParameterCount(); i < max; i++ ) { + final OrdinalParameterDescriptor descriptor = + queryImpl.getParameterMetadata().getOrdinalParameterDescriptor( i+1 ); + ParameterImpl parameter = new ParameterImpl( + descriptor.getOrdinalPosition() + 1, + descriptor.getExpectedType() == null + ? null + : descriptor.getExpectedType().getReturnedClass() + ); + parameters.add( parameter ); + Integer position = descriptor.getOrdinalPosition(); + if ( jpaPositionalIndices != null && jpaPositionalIndices.contains( position ) ) { + log.warn( "Parameter position [" + position + "] occurred as both JPA and Hibernate positional parameter" ); + } + } + + this.parameters = java.util.Collections.unmodifiableSet( parameters ); + } + + private static class ParameterImpl implements Parameter { + private final String name; + private final Integer position; + private final Class javaType; + + private ParameterImpl(String name, Class javaType) { + this.name = name; + this.javaType = javaType; + this.position = null; + } + + private ParameterImpl(Integer position, Class javaType) { + this.position = position; + this.javaType = javaType; + this.name = null; + } + + public String getName() { + return name; + } + + public Integer getPosition() { + return position; + } + + public Class getJavaType() { + return javaType; + } } public org.hibernate.Query getHibernateQuery() { return query; } - public int executeUpdate() { + protected int internalExecuteUpdate() { + return query.executeUpdate(); + } + + protected void applyMaxResults(int maxResults) { + query.setMaxResults( maxResults ); + } + + protected void applyFirstResult(int firstResult) { + query.setFirstResult( firstResult ); + } + + protected void applyTimeout(int timeout) { + query.setTimeout( timeout ); + } + + protected void applyComment(String comment) { + query.setComment( comment ); + } + + protected void applyFetchSize(int fetchSize) { + query.setFetchSize( fetchSize ); + } + + protected void applyCacheable(boolean isCacheable) { + query.setCacheable( isCacheable ); + } + + protected void applyCacheRegion(String regionName) { + query.setCacheRegion( regionName ); + } + + protected void applyReadOnly(boolean isReadOnly) { + query.setReadOnly( isReadOnly ); + } + + protected void applyCacheMode(CacheMode cacheMode) { + query.setCacheMode( cacheMode ); + } + + protected void applyFlushMode(FlushMode flushMode) { + query.setFlushMode( flushMode ); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings({ "unchecked" }) + public List getResultList() { try { - if ( ! em.isTransactionInProgress() ) { - em.throwPersistenceException( new TransactionRequiredException( "Executing an update/delete query" ) ); - return 0; - } - return query.executeUpdate(); + return (List) query.list(); } catch (QueryExecutionRequestException he) { throw new IllegalStateException(he); @@ -83,56 +217,39 @@ public class QueryImpl implements Query, HibernateQuery { throw new IllegalArgumentException(e); } catch (HibernateException he) { - em.throwPersistenceException( he ); - return 0; + throw getEntityManager().convert( he ); } } - public List getResultList() { + /** + * {@inheritDoc} + */ + @SuppressWarnings({ "unchecked" }) + public X getSingleResult() { try { - return query.list(); - } - catch (QueryExecutionRequestException he) { - throw new IllegalStateException(he); - } - catch( TypeMismatchException e ) { - throw new IllegalArgumentException(e); - } - catch (HibernateException he) { - em.throwPersistenceException( he ); - return null; - } - } - - public Object getSingleResult() { - try { - List result; - /* Avoid OOME if the list() is huge (user faulty query) by limiting the query to 2 elements max */ - //FIXME: get rid of this impl binding (HHH-3432) - if ( query instanceof AbstractQueryImpl ) { - if (maxResults != 1) query.setMaxResults( 2 ); //avoid OOME if the list is huge - result = query.list(); - if ( maxResults != -1 ) { - query.setMaxResults( maxResults ); //put back the original value - } - else { - AbstractQueryImpl queryImpl = AbstractQueryImpl.class.cast( query ); - queryImpl.getSelection().setMaxRows( null ); - } + boolean mucked = false; + // IMPL NOTE : the mucking with max results here is attempting to help the user from shooting themselves + // in the foot in the case where they have a large query by limiting the query results to 2 max + if ( getSpecifiedMaxResults() != 1 ) { + mucked = true; + query.setMaxResults( 2 ); //avoid OOME if the list is huge } - else { - //we can't do much because we cannot reset the maxResults => do the full list call - //Not tremendously bad as the user is doing a fault here anyway by calling getSingleREsults on a big list - result = query.list(); + List result = (List) query.list(); + if ( mucked ) { + query.setMaxResults( getSpecifiedMaxResults() ); } if ( result.size() == 0 ) { - em.throwPersistenceException( new NoResultException( "No entity found for query" ) ); + NoResultException nre = new NoResultException( "No entity found for query" ); + getEntityManager().handlePersistenceException( nre ); + throw nre; } else if ( result.size() > 1 ) { - Set uniqueResult = new HashSet(result); + Set uniqueResult = new HashSet(result); if ( uniqueResult.size() > 1 ) { - em.throwPersistenceException( new NonUniqueResultException( "result returns more than one elements") ); + NonUniqueResultException nure = new NonUniqueResultException( "result returns more than one elements" ); + getEntityManager().handlePersistenceException( nure ); + throw nure; } else { return uniqueResult.iterator().next(); @@ -140,9 +257,8 @@ public class QueryImpl implements Query, HibernateQuery { } else { - return result.get(0); + return result.get( 0 ); } - return null; //should never happen } catch (QueryExecutionRequestException he) { throw new IllegalStateException(he); @@ -151,93 +267,56 @@ public class QueryImpl implements Query, HibernateQuery { throw new IllegalArgumentException(e); } catch (HibernateException he) { - em.throwPersistenceException( he ); - return null; + throw getEntityManager().convert( he ); } } - public Query setMaxResults(int maxResult) { - if ( maxResult < 0 ) { - throw new IllegalArgumentException( - "Negative (" - + maxResult - + ") parameter passed in to setMaxResults" - ); + public TypedQuery setParameter(Parameter param, T value) { + if ( ! parameters.contains( param ) ) { + throw new IllegalArgumentException( "Specified parameter was not found in query" ); } - this.maxResults = maxResult; - query.setMaxResults( maxResult ); - return this; - } - - public int getMaxResults() { - return maxResults == -1 ? Integer.MAX_VALUE : maxResults; //stupid spec MAX_VALUE?? - } - - public Query setFirstResult(int firstResult) { - if ( firstResult < 0 ) { - throw new IllegalArgumentException( - "Negative (" - + firstResult - + ") parameter passed in to setFirstResult" - ); + if ( param.getName() != null ) { + // a named param, for not delegate out. Eventually delegate *into* this method... + setParameter( param.getName(), value ); } - query.setFirstResult( firstResult ); - this.firstResult = firstResult; - return this; - } - - public int getFirstResult() { - return firstResult; - } - - public Query setHint(String hintName, Object value) { - try { - if ( "org.hibernate.timeout".equals( hintName ) ) { - query.setTimeout( ConfigurationHelper.getInteger( value ) ); - } - else if ( "org.hibernate.comment".equals( hintName ) ) { - query.setComment( (String) value ); - } - else if ( "org.hibernate.fetchSize".equals( hintName ) ) { - query.setFetchSize( ConfigurationHelper.getInteger( value ) ); - } - else if ( "org.hibernate.cacheRegion".equals( hintName ) ) { - query.setCacheRegion( (String) value ); - } - else if ( "org.hibernate.cacheable".equals( hintName ) ) { - query.setCacheable( ConfigurationHelper.getBoolean( value ) ); - } - else if ( "org.hibernate.readOnly".equals( hintName ) ) { - query.setReadOnly( ConfigurationHelper.getBoolean( value ) ); - } - else if ( "org.hibernate.cacheMode".equals( hintName ) ) { - query.setCacheMode( ConfigurationHelper.getCacheMode( value ) ); - } - else if ( "org.hibernate.flushMode".equals( hintName ) ) { - query.setFlushMode( ConfigurationHelper.getFlushMode( value ) ); - } - //TODO: - /*else if ( "org.hibernate.lockMode".equals( hintName ) ) { - query.setLockMode( alias, lockMode ); - }*/ - } - catch (ClassCastException e) { - throw new IllegalArgumentException( "Value for hint" ); + else { + setParameter( param.getPosition(), value ); } return this; } - public Map getHints() { - //FIXME - return null; //To change body of implemented methods use File | Settings | File Templates. + public TypedQuery setParameter(Parameter param, Date value, TemporalType temporalType) { + if ( ! parameters.contains( param ) ) { + throw new IllegalArgumentException( "Specified parameter was not found in query" ); + } + if ( param.getName() != null ) { + // a named param, for not delegate out. Eventually delegate *into* this method... + setParameter( param.getName(), value, temporalType ); + } + else { + setParameter( param.getPosition(), value, temporalType ); + } + return this; } - public Set getSupportedHints() { - //FIXME - return null; //To change body of implemented methods use File | Settings | File Templates. + public TypedQuery setParameter(Parameter param, Calendar value, TemporalType temporalType) { + if ( ! parameters.contains( param ) ) { + throw new IllegalArgumentException( "Specified parameter was not found in query" ); + } + if ( param.getName() != null ) { + // a named param, for not delegate out. Eventually delegate *into* this method... + setParameter( param.getName(), value, temporalType ); + } + else { + setParameter( param.getPosition(), value, temporalType ); + } + return this; } - public Query setParameter(String name, Object value) { + /** + * {@inheritDoc} + */ + public TypedQuery setParameter(String name, Object value) { try { if ( value instanceof Collection ) { query.setParameterList( name, (Collection) value ); @@ -245,18 +324,21 @@ public class QueryImpl implements Query, HibernateQuery { else { query.setParameter( name, value ); } + registerParameterBinding( getParameter( name ), value ); return this; } catch (QueryParameterException e) { throw new IllegalArgumentException( e ); } catch (HibernateException he) { - em.throwPersistenceException( he ); - return null; + throw getEntityManager().convert( he ); } } - public Query setParameter(String name, Date value, TemporalType temporalType) { + /** + * {@inheritDoc} + */ + public TypedQuery setParameter(String name, Date value, TemporalType temporalType) { try { if ( temporalType == DATE ) { query.setDate( name, value ); @@ -267,18 +349,21 @@ public class QueryImpl implements Query, HibernateQuery { else if ( temporalType == TIMESTAMP ) { query.setTimestamp( name, value ); } + registerParameterBinding( getParameter( name ), value ); return this; } catch (QueryParameterException e) { throw new IllegalArgumentException( e ); } catch (HibernateException he) { - em.throwPersistenceException( he ); - return null; + throw getEntityManager().convert( he ); } } - public Query setParameter(String name, Calendar value, TemporalType temporalType) { + /** + * {@inheritDoc} + */ + public TypedQuery setParameter(String name, Calendar value, TemporalType temporalType) { try { if ( temporalType == DATE ) { query.setCalendarDate( name, value ); @@ -289,24 +374,28 @@ public class QueryImpl implements Query, HibernateQuery { else if ( temporalType == TIMESTAMP ) { query.setCalendar( name, value ); } + registerParameterBinding( getParameter(name), value ); return this; } catch (QueryParameterException e) { throw new IllegalArgumentException( e ); } catch (HibernateException he) { - em.throwPersistenceException( he ); - return null; + throw getEntityManager().convert( he ); } } - public Query setParameter(int position, Object value) { + /** + * {@inheritDoc} + */ + public TypedQuery setParameter(int position, Object value) { try { - if ( isPositionalParameter() ) { + if ( isJpaPositionalParameter( position ) ) { this.setParameter( Integer.toString( position ), value ); } else { query.setParameter( position - 1, value ); + registerParameterBinding( getParameter( position ), value ); } return this; } @@ -314,35 +403,20 @@ public class QueryImpl implements Query, HibernateQuery { throw new IllegalArgumentException( e ); } catch (HibernateException he) { - em.throwPersistenceException( he ); - return null; + throw getEntityManager().convert( he ); } } - private boolean isPositionalParameter() { - if (isPositional == null) { - //compute it - String queryString = query.getQueryString(); - int index = queryString.indexOf( '?' ); - //there is a ? and the following char is a digit - if (index == -1) { - //no ? - isPositional = true; - } - else if ( index == queryString.length() - 1 ) { - // "... ?" - isPositional = false; - } - else { - isPositional = Character.isDigit( queryString.charAt( index + 1 ) ); - } - } - return isPositional; + private boolean isJpaPositionalParameter(int position) { + return jpaPositionalIndices != null && jpaPositionalIndices.contains( position ); } - public Query setParameter(int position, Date value, TemporalType temporalType) { + /** + * {@inheritDoc} + */ + public TypedQuery setParameter(int position, Date value, TemporalType temporalType) { try { - if ( isPositionalParameter() ) { + if ( isJpaPositionalParameter( position ) ) { String name = Integer.toString( position ); this.setParameter( name, value, temporalType ); } @@ -356,6 +430,7 @@ public class QueryImpl implements Query, HibernateQuery { else if ( temporalType == TIMESTAMP ) { query.setTimestamp( position - 1, value ); } + registerParameterBinding( getParameter( position ), value ); } return this; } @@ -363,14 +438,16 @@ public class QueryImpl implements Query, HibernateQuery { throw new IllegalArgumentException( e ); } catch (HibernateException he) { - em.throwPersistenceException( he ); - return null; + throw getEntityManager().convert( he ); } } - public Query setParameter(int position, Calendar value, TemporalType temporalType) { + /** + * {@inheritDoc} + */ + public TypedQuery setParameter(int position, Calendar value, TemporalType temporalType) { try { - if ( isPositionalParameter() ) { + if ( isJpaPositionalParameter( position ) ) { String name = Integer.toString( position ); this.setParameter( name, value, temporalType ); } @@ -384,6 +461,7 @@ public class QueryImpl implements Query, HibernateQuery { else if ( temporalType == TIMESTAMP ) { query.setCalendar( position - 1, value ); } + registerParameterBinding( getParameter( position ), value ); } return this; } @@ -391,83 +469,117 @@ public class QueryImpl implements Query, HibernateQuery { throw new IllegalArgumentException( e ); } catch (HibernateException he) { - em.throwPersistenceException( he ); - return null; + throw getEntityManager().convert( he ); } } - //FIXME + /** + * {@inheritDoc} + */ public Set> getParameters() { - return null; //To change body of implemented methods use File | Settings | File Templates. + return parameters; } - //FIXME + /** + * {@inheritDoc} + */ public Parameter getParameter(String name) { - return null; //To change body of implemented methods use File | Settings | File Templates. + if ( name == null ) { + throw new IllegalArgumentException( "Name of parameter to locate cannot be null" ); + } + for ( Parameter parameter : parameters ) { + if ( name.equals( parameter.getName() ) ) { + return parameter; + } + } + throw new IllegalArgumentException( "Unable to locate parameter named [" + name + "]" ); } - //FIXME + /** + * {@inheritDoc} + */ public Parameter getParameter(int position) { - return null; //To change body of implemented methods use File | Settings | File Templates. + if ( isJpaPositionalParameter( position ) ) { + return getParameter( Integer.toString( position ) ); + } + else { + for ( Parameter parameter : parameters ) { + if ( parameter.getPosition() != null && position == parameter.getPosition() ) { + return parameter; + } + } + throw new IllegalArgumentException( "Unable to locate parameter with position [" + position + "]" ); + } } - //FIXME + /** + * {@inheritDoc} + */ + @SuppressWarnings({ "unchecked" }) public Parameter getParameter(String name, Class type) { - return null; //To change body of implemented methods use File | Settings | File Templates. + Parameter param = getParameter( name ); + if ( param.getJavaType() != null ) { + // we were able to determine the expected type during analysis, so validate it here + throw new IllegalArgumentException( + "Parameter type [" + param.getJavaType().getName() + + "] is not assignment compatible with requested type [" + + type.getName() + "]" + ); + } + return param; } - //FIXME + /** + * {@inheritDoc} + */ + @SuppressWarnings({ "unchecked" }) public Parameter getParameter(int position, Class type) { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - //FIXME - public boolean isBound(Parameter param) { - return false; //To change body of implemented methods use File | Settings | File Templates. - } - - //FIXME - public T getParameterValue(Parameter param) { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - //FIXME - public Object getParameterValue(String name) { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - //FIXME - public Object getParameterValue(int position) { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public Query setFlushMode(FlushModeType flushMode) { - if ( flushMode == FlushModeType.AUTO ) { - query.setFlushMode( FlushMode.AUTO ); + Parameter param = getParameter( position ); + if ( param.getJavaType() != null ) { + // we were able to determine the expected type during analysis, so validate it here + throw new IllegalArgumentException( + "Parameter type [" + param.getJavaType().getName() + + "] is not assignment compatible with requested type [" + + type.getName() + "]" + ); } - else if ( flushMode == FlushModeType.COMMIT ) { - query.setFlushMode( FlushMode.COMMIT ); - } - return this; + return param; } - public FlushModeType getFlushMode() { - //FIXME - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public Query setLockMode(LockModeType lockModeType) { - //FIXME - return null; //To change body of implemented methods use File | Settings | File Templates. + /** + * {@inheritDoc} + */ + public TypedQuery setLockMode(LockModeType lockModeType) { + // TODO : aye aye aye + throw new UnsupportedOperationException( "Not yet implemented" ); } + /** + * {@inheritDoc} + */ public LockModeType getLockMode() { - //FIXME - return null; //To change body of implemented methods use File | Settings | File Templates. + return LockModeType.NONE; } + /** + * {@inheritDoc} + */ + @SuppressWarnings({ "unchecked" }) public T unwrap(Class tClass) { - //FIXME - return null; //To change body of implemented methods use File | Settings | File Templates. + if ( org.hibernate.Query.class.isAssignableFrom( tClass ) ) { + return (T) query; + } + else { + try { + return (T) this; + } + catch ( ClassCastException cce ) { + PersistenceException pe = new PersistenceException( + "Unsupported unwrap target type [" + tClass.getName() + "]" + ); + getEntityManager().handlePersistenceException( pe ); + throw pe; + } + } } }