diff --git a/build.gradle b/build.gradle index 58845fc8cd..a20dbde8d8 100644 --- a/build.gradle +++ b/build.gradle @@ -71,7 +71,7 @@ libraries = [ javassist: 'org.javassist:javassist:3.15.0-GA', // javax - jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Draft-6b', + jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Draft-7plus', jta: 'org.jboss.spec.javax.transaction:jboss-transaction-api_1.1_spec:1.0.0.Final', validation: 'javax.validation:validation-api:1.0.0.GA', jacc: 'org.jboss.spec.javax.security.jacc:jboss-jacc-api_1.4_spec:1.0.0.Final', diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/QueryExecutionRequestException.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/QueryExecutionRequestException.java index f29a1650f5..c20f56e9fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/QueryExecutionRequestException.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/QueryExecutionRequestException.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 2012, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. + * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU @@ -20,9 +20,9 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.hql.internal; + import org.hibernate.QueryException; /** @@ -31,7 +31,6 @@ * @author Emmanuel Bernard */ public class QueryExecutionRequestException extends QueryException { - public QueryExecutionRequestException(String message, String queryString) { super( message, queryString ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/AbstractManipulationCriteriaQuery.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/AbstractManipulationCriteriaQuery.java index 20744dc25b..514f70bc73 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/AbstractManipulationCriteriaQuery.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/AbstractManipulationCriteriaQuery.java @@ -24,6 +24,7 @@ package org.hibernate.jpa.criteria; import javax.persistence.Query; +import javax.persistence.criteria.CommonAbstractCriteria; import javax.persistence.criteria.Expression; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; @@ -32,7 +33,6 @@ import java.util.List; import java.util.Map; -import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.jpa.internal.QueryImpl; import org.hibernate.jpa.criteria.compile.CompilableCriteria; import org.hibernate.jpa.criteria.compile.CriteriaInterpretation; @@ -48,7 +48,7 @@ * * @author Steve Ebersole */ -public abstract class AbstractManipulationCriteriaQuery implements CompilableCriteria { +public abstract class AbstractManipulationCriteriaQuery implements CompilableCriteria, CommonAbstractCriteria { private final CriteriaBuilderImpl criteriaBuilder; private RootImpl root; @@ -101,9 +101,7 @@ public Predicate getRestriction() { } public Subquery subquery(Class type) { - // Need clarification on spec in terms of how the built Subquery.getParent should be handled since getParent - // returns an AbstractQuery whereas neither CriteriaUpdate nor CriteriaDelete extend AbstractQuery - throw new NotYetImplementedException( "Need clarification on spec" ); + return new CriteriaSubqueryImpl( criteriaBuilder(), type, this ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/AbstractNode.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/AbstractNode.java index b1fc6be063..b2f31bdc5f 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/AbstractNode.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/AbstractNode.java @@ -38,11 +38,11 @@ public AbstractNode(CriteriaBuilderImpl criteriaBuilder) { } /** - * Provides protected access to the underlying {@link CriteriaBuilderImpl}. + * Provides access to the underlying {@link CriteriaBuilderImpl}. * * @return The underlying {@link CriteriaBuilderImpl} instance. */ - protected CriteriaBuilderImpl criteriaBuilder() { + public CriteriaBuilderImpl criteriaBuilder() { return criteriaBuilder; } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/CriteriaSubqueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/CriteriaSubqueryImpl.java index 93712228f6..98358b32ca 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/CriteriaSubqueryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/CriteriaSubqueryImpl.java @@ -28,6 +28,7 @@ import java.util.Set; import javax.persistence.criteria.AbstractQuery; import javax.persistence.criteria.CollectionJoin; +import javax.persistence.criteria.CommonAbstractCriteria; import javax.persistence.criteria.Expression; import javax.persistence.criteria.Join; import javax.persistence.criteria.ListJoin; @@ -40,6 +41,7 @@ import javax.persistence.metamodel.EntityType; import org.hibernate.jpa.criteria.compile.RenderingContext; +import org.hibernate.jpa.criteria.expression.DelegatedExpressionImpl; import org.hibernate.jpa.criteria.expression.ExpressionImpl; import org.hibernate.jpa.criteria.path.RootImpl; @@ -50,37 +52,39 @@ * @author Steve Ebersole */ public class CriteriaSubqueryImpl extends ExpressionImpl implements Subquery, Serializable { - private final AbstractQuery parent; + private final CommonAbstractCriteria parent; private final QueryStructure queryStructure; public CriteriaSubqueryImpl( CriteriaBuilderImpl criteriaBuilder, Class javaType, - AbstractQuery parent) { + CommonAbstractCriteria parent) { super( criteriaBuilder, javaType); this.parent = parent; this.queryStructure = new QueryStructure( this, criteriaBuilder ); } - /** - * {@inheritDoc} - */ + @Override public AbstractQuery getParent() { + if ( ! AbstractQuery.class.isInstance( parent ) ) { + throw new IllegalStateException( "Cannot call getParent on update/delete criterias" ); + } + return (AbstractQuery) parent; + } + + @Override + public CommonAbstractCriteria getContainingQuery() { return parent; } - /** - * {@inheritDoc} - */ + @Override public void registerParameters(ParameterRegistry registry) { for ( ParameterExpression param : queryStructure.getParameters() ) { registry.registerParameter( param ); } } - /** - * {@inheritDoc} - */ + @Override public Class getResultType() { return getJavaType(); } @@ -88,23 +92,17 @@ public Class getResultType() { // ROOTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - /** - * {@inheritDoc} - */ + @Override public Set> getRoots() { return queryStructure.getRoots(); } - /** - * {@inheritDoc} - */ + @Override public Root from(EntityType entityType) { return queryStructure.from( entityType ); } - /** - * {@inheritDoc} - */ + @Override public Root from(Class entityClass) { return queryStructure.from( entityClass ); } @@ -112,45 +110,71 @@ public Root from(Class entityClass) { // SELECTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + @Override public Subquery distinct(boolean applyDistinction) { queryStructure.setDistinct( applyDistinction ); return this; } + @Override public boolean isDistinct() { return queryStructure.isDistinct(); } + private Expression wrappedSelection; + + @Override public Expression getSelection() { - return (Expression) queryStructure.getSelection(); + if ( wrappedSelection == null ) { + if ( queryStructure.getSelection() == null ) { + return null; + } + wrappedSelection = new SubquerySelection( (ExpressionImpl) queryStructure.getSelection(), this ); + } + return wrappedSelection; } + @Override public Subquery select(Expression expression) { queryStructure.setSelection( expression ); return this; } + public static class SubquerySelection extends DelegatedExpressionImpl { + private final CriteriaSubqueryImpl subQuery; + + public SubquerySelection(ExpressionImpl wrapped, CriteriaSubqueryImpl subQuery) { + super( wrapped ); + this.subQuery = subQuery; + } + + @Override + public String render(RenderingContext renderingContext) { + return subQuery.render( renderingContext ); + } + + @Override + public String renderProjection(RenderingContext renderingContext) { + return render( renderingContext ); + } + } + + // RESTRICTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - /** - * {@inheritDoc} - */ + @Override public Predicate getRestriction() { return queryStructure.getRestriction(); } - /** - * {@inheritDoc} - */ + @Override public Subquery where(Expression expression) { queryStructure.setRestriction( criteriaBuilder().wrap( expression ) ); return this; } - /** - * {@inheritDoc} - */ + @Override public Subquery where(Predicate... predicates) { // TODO : assuming this should be a conjuntion, but the spec does not say specifically... queryStructure.setRestriction( criteriaBuilder().and( predicates ) ); @@ -161,47 +185,35 @@ public Subquery where(Predicate... predicates) { // GROUPING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - /** - * {@inheritDoc} - */ + @Override public List> getGroupList() { return queryStructure.getGroupings(); } - /** - * {@inheritDoc} - */ + @Override public Subquery groupBy(Expression... groupings) { queryStructure.setGroupings( groupings ); return this; } - /** - * {@inheritDoc} - */ + @Override public Subquery groupBy(List> groupings) { queryStructure.setGroupings( groupings ); return this; } - /** - * {@inheritDoc} - */ + @Override public Predicate getGroupRestriction() { return queryStructure.getHaving(); } - /** - * {@inheritDoc} - */ + @Override public Subquery having(Expression expression) { queryStructure.setHaving( criteriaBuilder().wrap( expression ) ); return this; } - /** - * {@inheritDoc} - */ + @Override public Subquery having(Predicate... predicates) { queryStructure.setHaving( criteriaBuilder().and( predicates ) ); return this; @@ -210,74 +222,62 @@ public Subquery having(Predicate... predicates) { // CORRELATIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - /** - * {@inheritDoc} - */ - public Set> getCorrelatedJoins() { + @Override + public Set> getCorrelatedJoins() { return queryStructure.collectCorrelatedJoins(); } - /** - * {@inheritDoc} - */ + @Override public Root correlate(Root source) { final RootImpl correlation = ( ( RootImpl ) source ).correlateTo( this ); queryStructure.addCorrelationRoot( correlation ); return correlation; } - /** - * {@inheritDoc} - */ + @Override public Join correlate(Join source) { final JoinImplementor correlation = ( (JoinImplementor) source ).correlateTo( this ); queryStructure.addCorrelationRoot( correlation ); return correlation; } - /** - * {@inheritDoc} - */ + @Override public CollectionJoin correlate(CollectionJoin source) { final CollectionJoinImplementor correlation = ( (CollectionJoinImplementor) source ).correlateTo( this ); queryStructure.addCorrelationRoot( correlation ); return correlation; } - /** - * {@inheritDoc} - */ + @Override public SetJoin correlate(SetJoin source) { final SetJoinImplementor correlation = ( (SetJoinImplementor) source ).correlateTo( this ); queryStructure.addCorrelationRoot( correlation ); return correlation; } - /** - * {@inheritDoc} - */ + @Override public ListJoin correlate(ListJoin source) { final ListJoinImplementor correlation = ( (ListJoinImplementor) source ).correlateTo( this ); queryStructure.addCorrelationRoot( correlation ); return correlation; } - /** - * {@inheritDoc} - */ + @Override public MapJoin correlate(MapJoin source) { final MapJoinImplementor correlation = ( (MapJoinImplementor) source ).correlateTo( this ); queryStructure.addCorrelationRoot( correlation ); return correlation; } - /** - * {@inheritDoc} - */ + @Override public Subquery subquery(Class subqueryType) { return queryStructure.subquery( subqueryType ); } + + // rendering ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override public String render(RenderingContext renderingContext) { StringBuilder subqueryBuffer = new StringBuilder( "(" ); queryStructure.render( subqueryBuffer, renderingContext ); @@ -285,6 +285,7 @@ public String render(RenderingContext renderingContext) { return subqueryBuffer.toString(); } + @Override public String renderProjection(RenderingContext renderingContext) { throw new IllegalStateException( "Subquery cannot occur in select clause" ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/ExpressionImplementor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/ExpressionImplementor.java index 6c3a12082c..438ab95dad 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/ExpressionImplementor.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/ExpressionImplementor.java @@ -22,12 +22,13 @@ * Boston, MA 02110-1301 USA */ package org.hibernate.jpa.criteria; + import java.math.BigDecimal; import java.math.BigInteger; import javax.persistence.criteria.Expression; /** - * TODO : javadoc + * Internal contract for implementations of the JPA {@link Expression} contract. * * @author Steve Ebersole */ diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/expression/DelegatedExpressionImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/expression/DelegatedExpressionImpl.java new file mode 100644 index 0000000000..f251706632 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/expression/DelegatedExpressionImpl.java @@ -0,0 +1,108 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.criteria.expression; + +import javax.persistence.criteria.Selection; +import java.util.List; + +import org.hibernate.jpa.criteria.ParameterRegistry; +import org.hibernate.jpa.criteria.ValueHandlerFactory; + +/** + * Implementation of {@link javax.persistence.criteria.Expression} wraps another Expression and delegates most of its + * functionality to that wrapped Expression + * + * @author Steve Ebersole + */ +public abstract class DelegatedExpressionImpl extends ExpressionImpl { + private final ExpressionImpl wrapped; + + public DelegatedExpressionImpl(ExpressionImpl wrapped) { + super( wrapped.criteriaBuilder(), wrapped.getJavaType() ); + this.wrapped = wrapped; + } + + public ExpressionImpl getWrapped() { + return wrapped; + } + + + // delegations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override + public void registerParameters(ParameterRegistry registry) { + wrapped.registerParameters( registry ); + } + + @Override + public Selection alias(String alias) { + wrapped.alias( alias ); + return this; + } + + @Override + public boolean isCompoundSelection() { + return wrapped.isCompoundSelection(); + } + + @Override + public List getValueHandlers() { + return wrapped.getValueHandlers(); + } + + @Override + public List> getCompoundSelectionItems() { + return wrapped.getCompoundSelectionItems(); + } + + @Override + public Class getJavaType() { + return wrapped.getJavaType(); + } + + @Override + protected void resetJavaType(Class targetType) { + wrapped.resetJavaType( targetType ); + } + + @Override + protected void forceConversion(ValueHandlerFactory.ValueHandler tValueHandler) { + wrapped.forceConversion( tValueHandler ); + } + + @Override + public ValueHandlerFactory.ValueHandler getValueHandler() { + return wrapped.getValueHandler(); + } + + @Override + public String getAlias() { + return wrapped.getAlias(); + } + + @Override + protected void setAlias(String alias) { + wrapped.setAlias( alias ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java index 00aed4b074..e3b9ebff46 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java @@ -30,6 +30,7 @@ import javax.persistence.PersistenceException; import javax.persistence.PersistenceUnitUtil; import javax.persistence.Query; +import javax.persistence.SynchronizationType; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.metamodel.Metamodel; import javax.persistence.spi.LoadState; @@ -49,6 +50,7 @@ import org.hibernate.SessionFactory; import org.hibernate.cache.spi.RegionFactory; import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.ejb.AvailableSettings; import org.hibernate.ejb.HibernateEntityManagerFactory; import org.hibernate.ejb.HibernateQuery; @@ -205,11 +207,21 @@ public EntityManager createEntityManager() { public EntityManager createEntityManager(Map map) { //TODO support discardOnClose, persistencecontexttype?, interceptor, return new EntityManagerImpl( - this, PersistenceContextType.EXTENDED, transactionType, - discardOnClose, sessionInterceptorClass, map + this, + PersistenceContextType.EXTENDED, + transactionType, + discardOnClose, + sessionInterceptorClass, + map ); } + @Override + public EntityManager createEntityManager(SynchronizationType synchronizationType, Map map) { + // todo : implement it + throw new NotYetImplementedException( "Not yet implemented" ); + } + public CriteriaBuilder getCriteriaBuilder() { return criteriaBuilder; } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/criteria/ManipulationCriteriaTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/criteria/ManipulationCriteriaTest.java index d55651b47d..a965b393fb 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/criteria/ManipulationCriteriaTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/criteria/ManipulationCriteriaTest.java @@ -24,10 +24,12 @@ package org.hibernate.ejb.criteria; import javax.persistence.EntityManager; +import javax.persistence.Query; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaDelete; import javax.persistence.criteria.CriteriaUpdate; import javax.persistence.criteria.Root; +import javax.persistence.criteria.Subquery; import org.hibernate.ejb.metamodel.AbstractMetamodelSpecificTest; import org.hibernate.ejb.metamodel.Customer; @@ -170,4 +172,48 @@ public void testJoinsAndFetchesDisallowed() { em.close(); } + + @Test + public void testDeleteWithUnCorrelatedSubquery() { + CriteriaBuilder builder = entityManagerFactory().getCriteriaBuilder(); + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + // attempt to delete Customers who's age is less than the AVG age + CriteriaDelete criteria = builder.createCriteriaDelete( Customer.class ); + Root customerRoot = criteria.from( Customer.class ); + + Subquery subCriteria = criteria.subquery( Double.class ); + Root subQueryCustomerRoot = subCriteria.from( Customer.class ); + subCriteria.select( builder.avg( subQueryCustomerRoot.get( Customer_.age ) ) ); + + // also illustrates the new capability to use the subquery selection as an expression! + criteria.where( + builder.lessThan( + customerRoot.get( Customer_.age ), + subCriteria.getSelection().as( Integer.class ) + ) + ); + + // make sure Subquery#getParent fails... + try { + subCriteria.getParent(); + fail( "Expecting Subquery.getParent call to fail on DELETE containing criteria" ); + } + catch (IllegalStateException expected) { + } + + Query query = em.createQuery( criteria ); + try { + // first, make sure an attempt to list fails + query.getResultList(); + fail( "Attempt to getResultList() on delete criteria should have failed" ); + } + catch (IllegalStateException expected) { + } + query.executeUpdate(); + + em.getTransaction().commit(); + em.close(); + } } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/criteria/subquery/UncorrelatedSubqueryTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/criteria/subquery/UncorrelatedSubqueryTest.java index 756baee32f..90302727ca 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/criteria/subquery/UncorrelatedSubqueryTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/criteria/subquery/UncorrelatedSubqueryTest.java @@ -24,20 +24,24 @@ package org.hibernate.ejb.criteria.subquery; import javax.persistence.EntityManager; +import javax.persistence.Query; import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaDelete; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Join; import javax.persistence.criteria.Root; import javax.persistence.criteria.Subquery; -import org.junit.Test; - import org.hibernate.ejb.metamodel.AbstractMetamodelSpecificTest; import org.hibernate.ejb.metamodel.Customer; import org.hibernate.ejb.metamodel.Customer_; import org.hibernate.ejb.metamodel.Order; import org.hibernate.ejb.metamodel.Order_; +import org.junit.Test; + +import static org.junit.Assert.fail; + /** * @author Steve Ebersole */ @@ -61,4 +65,29 @@ public void testEqualAll() { em.getTransaction().commit(); em.close(); } + + @Test + public void testLessThan() { + CriteriaBuilder builder = entityManagerFactory().getCriteriaBuilder(); + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + CriteriaQuery criteria = builder.createQuery( Customer.class ); + Root customerRoot = criteria.from( Customer.class ); + + Subquery subCriteria = criteria.subquery( Double.class ); + Root subQueryCustomerRoot = subCriteria.from( Customer.class ); + subCriteria.select( builder.avg( subQueryCustomerRoot.get( Customer_.age ) ) ); + + criteria.where( + builder.lessThan( + customerRoot.get( Customer_.age ), + subCriteria.getSelection().as( Integer.class ) + ) + ); + em.createQuery( criteria ).getResultList(); + + em.getTransaction().commit(); + em.close(); + } }