/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010 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 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; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.persistence.CacheRetrieveMode; import javax.persistence.CacheStoreMode; import javax.persistence.FlushModeType; import javax.persistence.Parameter; import javax.persistence.TransactionRequiredException; import javax.persistence.TypedQuery; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.TypeMismatchException; 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 org.hibernate.hql.internal.QueryExecutionRequestException; import org.jboss.logging.Logger; /** * Intended as a base class providing convenience in implementing both {@link javax.persistence.Query} and * {@link javax.persistence.TypedQuery}. * <p/> * 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<X> implements TypedQuery<X> { private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, AbstractQueryImpl.class.getName()); 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<X> 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<X> 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<String, Object> hints; /** * {@inheritDoc} */ public Map<String, Object> 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); protected abstract boolean canApplyLockModes(); protected abstract void applyAliasSpecificLockMode(String alias, LockMode lockMode); /** * {@inheritDoc} */ public TypedQuery<X> setHint(String hintName, Object value) { boolean skipped = false; try { if ( HINT_TIMEOUT.equals( hintName ) ) { applyTimeout( 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 ); applyTimeout( new Integer(timeout) ); } 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 ) ); } 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 ); } applyCacheMode( 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 ); } applyCacheMode( CacheModeHelper.interpretCacheMode( storeMode, retrieveMode ) ); } else if ( hintName.startsWith( AvailableSettings.ALIAS_SPECIFIC_LOCK_MODE ) ) { if ( ! canApplyLockModes() ) { skipped = true; } else { // 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 ); applyAliasSpecificLockMode( alias, lockMode ); } catch ( Exception e ) { LOG.unableToDetermineLockModeValue(hintName, value); skipped = true; } } } else { skipped = true; LOG.ignoringUnrecognizedQueryHint(hintName); } } catch ( ClassCastException e ) { throw new IllegalArgumentException( "Value for hint" ); } if ( !skipped ) { if ( hints == null ) { hints = new HashMap<String,Object>(); } hints.put( hintName, value ); } return this; } public Set<String> getSupportedHints() { return QueryHints.getDefinedHints(); } public abstract TypedQuery<X> setLockMode(javax.persistence.LockModeType lockModeType); public abstract javax.persistence.LockModeType getLockMode(); private FlushModeType jpaFlushMode; public TypedQuery<X> 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.getParameterType() != null ) { if ( Collection.class.isInstance( value ) ) { final Collection collection = (Collection) value; // validate the elements... for ( Object element : collection ) { if ( ! parameter.getParameterType().isInstance( element ) ) { throw new IllegalArgumentException( "Parameter value [" + element + "] was not matching type [" + parameter.getParameterType().getName() + "]" ); } } } else if ( value.getClass().isArray() && value.getClass().equals( Object[].class ) ) { final Object[] array = (Object[]) value; for ( Object element : array ) { if ( ! parameter.getParameterType().isInstance( element ) ) { throw new IllegalArgumentException( "Parameter value [" + element + "] was not matching type [" + parameter.getParameterType().getName() + "]" ); } } } else { if ( ! parameter.getParameterType().isInstance( value ) ) { throw new IllegalArgumentException( "Parameter value [" + value + "] was not matching type [" + parameter.getParameterType().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> T getParameterValue(Parameter<T> 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 ) ); } }