/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.query.criteria.internal.compile; import java.io.Serializable; import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; import javax.persistence.FlushModeType; import javax.persistence.LockModeType; import javax.persistence.Parameter; import javax.persistence.TemporalType; import javax.persistence.criteria.ParameterExpression; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.query.ParameterMetadata; import org.hibernate.query.QueryParameter; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.spi.QueryProducerImplementor; import org.hibernate.transform.ResultTransformer; import org.hibernate.type.Type; /** * <strong>Make this go away in 6.0</strong> :) * <p/> * Needed because atm we render a JPA Criteria query into a HQL/JPQL query String and some metadata, and then * compile into a Query. This class wraps the compiled HQL/JPQL query and adds an extra layer of metadata. * <p/> * But the move to SQM in 6.0 allows us to do away with the "wrapping". * * Essentially * * @author Steve Ebersole */ public class CriteriaQueryTypeQueryAdapter<X> implements QueryImplementor<X> { private final SessionImplementor entityManager; private final QueryImplementor<X> jpqlQuery; private final Map<ParameterExpression<?>, ExplicitParameterInfo<?>> explicitParameterInfoMap; public CriteriaQueryTypeQueryAdapter( SessionImplementor entityManager, QueryImplementor<X> jpqlQuery, Map<ParameterExpression<?>, ExplicitParameterInfo<?>> explicitParameterInfoMap) { this.entityManager = entityManager; this.jpqlQuery = jpqlQuery; this.explicitParameterInfoMap = explicitParameterInfoMap; } public List<X> getResultList() { return jpqlQuery.getResultList(); } @Override public X uniqueResult() { return jpqlQuery.uniqueResult(); } @Override public Optional<X> uniqueResultOptional() { return jpqlQuery.uniqueResultOptional(); } @Override public Stream<X> stream() { return jpqlQuery.stream(); } @Override public List<X> list() { return jpqlQuery.list(); } @Override public QueryImplementor<X> setCacheMode(CacheMode cacheMode) { jpqlQuery.setCacheMode( cacheMode ); return this; } @Override public boolean isCacheable() { return jpqlQuery.isCacheable(); } public X getSingleResult() { return jpqlQuery.getSingleResult(); } @Override public ParameterMetadata getParameterMetadata() { return jpqlQuery.getParameterMetadata(); } @Override public String[] getNamedParameters() { return jpqlQuery.getNamedParameters(); } public int getMaxResults() { return jpqlQuery.getMaxResults(); } public QueryImplementor<X> setMaxResults(int maxResult) { jpqlQuery.setMaxResults( maxResult ); return this; } public int getFirstResult() { return jpqlQuery.getFirstResult(); } public QueryImplementor<X> setFirstResult(int i) { jpqlQuery.setFirstResult( i ); return this; } public Map<String, Object> getHints() { return jpqlQuery.getHints(); } public QueryImplementor<X> setHint(String name, Object value) { jpqlQuery.setHint( name, value ); return this; } protected boolean isNativeQuery() { return false; } @Override public String getQueryString() { return jpqlQuery.getQueryString(); } @Override public FlushMode getHibernateFlushMode() { return jpqlQuery.getHibernateFlushMode(); } @Override public FlushModeType getFlushMode() { return jpqlQuery.getFlushMode(); } @Override public CacheMode getCacheMode() { return jpqlQuery.getCacheMode(); } @Override public Type[] getReturnTypes() { return jpqlQuery.getReturnTypes(); } @Override public LockOptions getLockOptions() { return jpqlQuery.getLockOptions(); } @Override public RowSelection getQueryOptions() { return jpqlQuery.getQueryOptions(); } @Override public QueryImplementor<X> setFlushMode(FlushModeType flushModeType) { jpqlQuery.setFlushMode( flushModeType ); return this; } @Override public QueryImplementor setFlushMode(FlushMode flushMode) { jpqlQuery.setFlushMode( flushMode ); return this; } @Override public QueryImplementor<X> setHibernateFlushMode(FlushMode flushMode) { jpqlQuery.setHibernateFlushMode( flushMode ); return this; } @Override public QueryImplementor setCacheable(boolean cacheable) { jpqlQuery.setCacheable( cacheable ); return this; } @Override public String getCacheRegion() { return jpqlQuery.getCacheRegion(); } @Override public QueryImplementor setCacheRegion(String cacheRegion) { jpqlQuery.setCacheRegion( cacheRegion ); return this; } @Override public Integer getTimeout() { return jpqlQuery.getTimeout(); } @Override public QueryImplementor setTimeout(int timeout) { jpqlQuery.setTimeout( timeout ); return this; } @Override public Integer getFetchSize() { return jpqlQuery.getFetchSize(); } @Override public QueryImplementor setLockOptions(LockOptions lockOptions) { jpqlQuery.setLockOptions( lockOptions ); return this; } @Override public QueryImplementor setLockMode(String alias, LockMode lockMode) { jpqlQuery.setLockMode( alias, lockMode ); return this; } @Override public String getComment() { return jpqlQuery.getComment(); } @Override public QueryImplementor setComment(String comment) { jpqlQuery.setComment( comment ); return this; } @Override public QueryImplementor addQueryHint(String hint) { jpqlQuery.addQueryHint( hint ); return this; } @Override public Iterator<X> iterate() { return jpqlQuery.iterate(); } @Override public ScrollableResults scroll() { return jpqlQuery.scroll(); } @Override public ScrollableResults scroll(ScrollMode scrollMode) { return jpqlQuery.scroll( scrollMode ); } @Override public QueryImplementor setFetchSize(int fetchSize) { jpqlQuery.setFetchSize( fetchSize ); return this; } @Override public boolean isReadOnly() { return jpqlQuery.isReadOnly(); } @Override public LockModeType getLockMode() { return jpqlQuery.getLockMode(); } @Override public QueryImplementor<X> setLockMode(LockModeType lockModeType) { jpqlQuery.setLockMode( lockModeType ); return this; } @Override public QueryImplementor setReadOnly(boolean readOnly) { jpqlQuery.setReadOnly( readOnly ); return this; } @Override public Type determineProperBooleanType(int position, Object value, Type defaultType) { return jpqlQuery.determineProperBooleanType( position, value, defaultType ); } @Override public Type determineProperBooleanType(String name, Object value, Type defaultType) { return jpqlQuery.determineProperBooleanType( name, value, defaultType ); } @Override public String[] getReturnAliases() { return jpqlQuery.getReturnAliases(); } @Override @SuppressWarnings({ "unchecked" }) public Set<Parameter<?>> getParameters() { entityManager.checkOpen( false ); return new HashSet( explicitParameterInfoMap.values() ); } @Override public boolean isBound(Parameter<?> param) { entityManager.checkOpen( false ); return jpqlQuery.isBound( param ); } @Override @SuppressWarnings({ "unchecked" }) public <T> T getParameterValue(Parameter<T> param) { entityManager.checkOpen( false ); final ExplicitParameterInfo parameterInfo = resolveParameterInfo( param ); if ( parameterInfo.isNamed() ) { return ( T ) jpqlQuery.getParameterValue( parameterInfo.getName() ); } else { return ( T ) jpqlQuery.getParameterValue( parameterInfo.getPosition() ); } } private <T> ExplicitParameterInfo resolveParameterInfo(Parameter<T> param) { if ( ExplicitParameterInfo.class.isInstance( param ) ) { return (ExplicitParameterInfo) param; } else if ( ParameterExpression.class.isInstance( param ) ) { return explicitParameterInfoMap.get( (ParameterExpression) param ); } else { for ( ExplicitParameterInfo parameterInfo : explicitParameterInfoMap.values() ) { if ( param.getName() != null && param.getName().equals( parameterInfo.getName() ) ) { return parameterInfo; } else if ( param.getPosition() != null && param.getPosition().equals( parameterInfo.getPosition() ) ) { return parameterInfo; } } } throw new IllegalArgumentException( "Unable to locate parameter [" + param + "] in query" ); } @Override @SuppressWarnings({ "unchecked" }) public <T> QueryImplementor<X> setParameter(Parameter<T> param, T t) { entityManager.checkOpen( false ); final ExplicitParameterInfo parameterInfo = resolveParameterInfo( param ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), t ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), t ); } return this; } @Override @SuppressWarnings({ "unchecked" }) public QueryImplementor<X> setParameter(Parameter<Calendar> param, Calendar calendar, TemporalType temporalType) { entityManager.checkOpen( false ); final ExplicitParameterInfo parameterInfo = resolveParameterInfo( param ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), calendar, temporalType ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), calendar, temporalType ); } return this; } @Override @SuppressWarnings({ "unchecked" }) public QueryImplementor<X> setParameter(Parameter<Date> param, Date date, TemporalType temporalType) { entityManager.checkOpen( false ); final ExplicitParameterInfo parameterInfo = resolveParameterInfo( param ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), date, temporalType ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), date, temporalType ); } return this; } @Override public <T> T unwrap(Class<T> cls) { return jpqlQuery.unwrap( cls ); } @Override @SuppressWarnings({ "unchecked" }) public Object getParameterValue(String name) { entityManager.checkOpen( false ); locateParameterByName( name ); return jpqlQuery.getParameterValue( name ); } private ExplicitParameterInfo locateParameterByName(String name) { for ( ExplicitParameterInfo parameterInfo : explicitParameterInfoMap.values() ) { if ( parameterInfo.isNamed() && parameterInfo.getName().equals( name ) ) { return parameterInfo; } } throw new IllegalArgumentException( "Unable to locate parameter registered with that name [" + name + "]" ); } private ExplicitParameterInfo locateParameterByPosition(int position) { for ( ExplicitParameterInfo parameterInfo : explicitParameterInfoMap.values() ) { if ( parameterInfo.getPosition() == position ) { return parameterInfo; } } throw new IllegalArgumentException( "Unable to locate parameter registered at position [" + position + "]" ); } public Parameter<?> getParameter(String name) { entityManager.checkOpen( false ); return locateParameterByName( name ); } @Override @SuppressWarnings({ "unchecked" }) public <T> Parameter<T> getParameter(String name, Class<T> type) { entityManager.checkOpen( false ); Parameter parameter = locateParameterByName( name ); if ( type.isAssignableFrom( parameter.getParameterType() ) ) { return parameter; } throw new IllegalArgumentException( "Named parameter [" + name + "] type is not assignanle to request type [" + type.getName() + "]" ); } @Override @SuppressWarnings({ "unchecked" }) public QueryImplementor<X> setParameter(String name, Object value) { entityManager.checkOpen( true ); ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateBindValue( value ); jpqlQuery.setParameter( name, value ); return this; } @Override @SuppressWarnings({ "unchecked" }) public QueryImplementor<X> setParameter(String name, Calendar calendar, TemporalType temporalType) { entityManager.checkOpen( true ); ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateCalendarBind(); jpqlQuery.setParameter( name, calendar, temporalType ); return this; } @Override @SuppressWarnings({ "unchecked" }) public QueryImplementor<X> setParameter(String name, Date date, TemporalType temporalType) { entityManager.checkOpen( true ); ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateDateBind(); jpqlQuery.setParameter( name, date, temporalType ); return this; } @Override public QueryImplementor<X> setEntity(String name, Object val) { ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateDateBind(); jpqlQuery.setEntity( name, val ); return this; } @Override public QueryImplementor<X> setParameter(String name, Object val, Type type) { ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateDateBind(); jpqlQuery.setParameter( name, val, type ); return this; } @Override public <T> QueryImplementor<X> setParameter(QueryParameter<T> parameter, T val) { final ExplicitParameterInfo parameterInfo = resolveParameterInfo( parameter ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), val ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), val ); } return this; } @Override public <P> QueryImplementor<X> setParameter( QueryParameter<P> parameter, P val, TemporalType temporalType) { final ExplicitParameterInfo parameterInfo = resolveParameterInfo( parameter ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), val, temporalType ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), val, temporalType ); } return this; } @Override public <P> QueryImplementor<X> setParameter(String name, P val, TemporalType temporalType) { ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateDateBind(); jpqlQuery.setParameter( name, val, temporalType ); return this; } @Override public <P> QueryImplementor<X> setParameterList(QueryParameter<P> parameter, Collection<P> values) { final ExplicitParameterInfo parameterInfo = resolveParameterInfo( parameter ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), values ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), values ); } return this; } @Override public QueryImplementor<X> setParameterList(String name, Collection values) { ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateDateBind(); jpqlQuery.setParameter( name, values ); return this; } @Override public QueryImplementor<X> setParameterList(String name, Collection values, Type type) { ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateDateBind(); jpqlQuery.setParameter( name, values, type ); return this; } @Override public QueryImplementor<X> setParameterList(String name, Object[] values, Type type) { ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateDateBind(); jpqlQuery.setParameter( name, values, type ); return this; } @Override public QueryImplementor<X> setParameterList(String name, Object[] values) { ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateDateBind(); jpqlQuery.setParameter( name, values ); return this; } @Override public <P> QueryImplementor<X> setParameter(QueryParameter<P> parameter, P value, Type type) { final ExplicitParameterInfo parameterInfo = resolveParameterInfo( parameter ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), value, type ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), value, type ); } return this; } @Override public QueryImplementor<X> setParameter(Parameter<Instant> param, Instant value, TemporalType temporalType){ final ExplicitParameterInfo parameterInfo = resolveParameterInfo( param ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), value, temporalType ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), value, temporalType ); } return this; } @Override public QueryImplementor<X> setParameter(Parameter<LocalDateTime> param, LocalDateTime value, TemporalType temporalType){ final ExplicitParameterInfo parameterInfo = resolveParameterInfo( param ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), value, temporalType ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), value, temporalType ); } return this; } @Override public QueryImplementor<X> setParameter(Parameter<ZonedDateTime> param, ZonedDateTime value, TemporalType temporalType){ final ExplicitParameterInfo parameterInfo = resolveParameterInfo( param ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), value, temporalType ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), value, temporalType ); } return this; } @Override public QueryImplementor<X> setParameter(Parameter<OffsetDateTime> param, OffsetDateTime value, TemporalType temporalType){ final ExplicitParameterInfo parameterInfo = resolveParameterInfo( param ); if ( parameterInfo.isNamed() ) { jpqlQuery.setParameter( parameterInfo.getName(), value, temporalType ); } else { jpqlQuery.setParameter( parameterInfo.getPosition(), value, temporalType ); } return this; } @Override public QueryImplementor<X> setParameter(String name, Instant value, TemporalType temporalType){ ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateCalendarBind(); jpqlQuery.setParameter( name, value, temporalType ); return this; } @Override public QueryImplementor<X> setParameter(String name, LocalDateTime value, TemporalType temporalType){ ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateCalendarBind(); jpqlQuery.setParameter( name, value, temporalType ); return this; } @Override public QueryImplementor<X> setParameter(String name, ZonedDateTime value, TemporalType temporalType){ ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateCalendarBind(); jpqlQuery.setParameter( name, value, temporalType ); return this; } @Override public QueryImplementor<X> setParameter(String name, OffsetDateTime value, TemporalType temporalType){ ExplicitParameterInfo parameterInfo = locateParameterByName( name ); parameterInfo.validateCalendarBind(); jpqlQuery.setParameter( name, value, temporalType ); return this; } @Override public QueryImplementor<X> setResultTransformer(ResultTransformer transformer) { jpqlQuery.setResultTransformer( transformer ); return this; } @Override public QueryImplementor<X> setProperties(Object bean) { jpqlQuery.setProperties( bean ); return this; } @Override public QueryImplementor setProperties(Map map) { jpqlQuery.setProperties( map ); return this; } @Override public QueryProducerImplementor getProducer() { return jpqlQuery.getProducer(); } @Override public void setOptionalId(Serializable id) { jpqlQuery.setOptionalId( id ); } @Override public void setOptionalEntityName(String entityName) { jpqlQuery.setOptionalEntityName( entityName ); } @Override public void setOptionalObject(Object optionalObject) { jpqlQuery.setOptionalObject( optionalObject ); } @Override public QueryImplementor<X> setParameter(int position, LocalDateTime value, TemporalType temporalType) { final ExplicitParameterInfo explicitParameterInfo = locateParameterByPosition( position ); explicitParameterInfo.validateDateBind(); jpqlQuery.setParameter( position, value, temporalType ); return this; } @Override public QueryImplementor<X> setParameter(int position, Instant value, TemporalType temporalType) { final ExplicitParameterInfo explicitParameterInfo = locateParameterByPosition( position ); explicitParameterInfo.validateDateBind(); jpqlQuery.setParameter( position, value, temporalType ); return this; } @Override public QueryImplementor<X> setParameter(int position, ZonedDateTime value, TemporalType temporalType) { final ExplicitParameterInfo explicitParameterInfo = locateParameterByPosition( position ); explicitParameterInfo.validateDateBind(); jpqlQuery.setParameter( position, value, temporalType ); return this; } @Override public QueryImplementor<X> setParameter(int position, OffsetDateTime value, TemporalType temporalType) { final ExplicitParameterInfo explicitParameterInfo = locateParameterByPosition( position ); explicitParameterInfo.validateDateBind(); jpqlQuery.setParameter( position, value, temporalType ); return this; } @Override public QueryImplementor<X> setParameter(int position, Object val, Type type) { final ExplicitParameterInfo explicitParameterInfo = locateParameterByPosition( position ); explicitParameterInfo.validateDateBind(); jpqlQuery.setParameter( position, val, type ); return this; } @Override public QueryImplementor<X> setEntity(int position, Object val) { final ExplicitParameterInfo explicitParameterInfo = locateParameterByPosition( position ); explicitParameterInfo.validateDateBind(); jpqlQuery.setParameter( position, val ); return this; } @Override public <P> QueryImplementor<X> setParameter(int position, P val, TemporalType temporalType) { final ExplicitParameterInfo explicitParameterInfo = locateParameterByPosition( position ); explicitParameterInfo.validateDateBind(); jpqlQuery.setParameter( position, val, temporalType ); return this; } // unsupported stuff ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public int executeUpdate() { throw new IllegalStateException( "Typed criteria queries do not support executeUpdate" ); } @Override public QueryImplementor<X> setParameter(int i, Object o) { throw new IllegalArgumentException( "Criteria queries do not support positioned parameters" ); } @Override public QueryImplementor<X> setParameter(int i, Calendar calendar, TemporalType temporalType) { throw new IllegalArgumentException( "Criteria queries do not support positioned parameters" ); } @Override public QueryImplementor<X> setParameter(int i, Date date, TemporalType temporalType) { throw new IllegalArgumentException( "Criteria queries do not support positioned parameters" ); } @Override public Object getParameterValue(int position) { throw new IllegalArgumentException( "Criteria queries do not support positioned parameters" ); } @Override public Parameter<?> getParameter(int position) { throw new IllegalArgumentException( "Criteria queries do not support positioned parameters" ); } public <T> Parameter<T> getParameter(int position, Class<T> type) { throw new IllegalArgumentException( "Criteria queries do not support positioned parameters" ); } }