HHH-7451 - Integrate Draft 7 of the JPA 2.1 spec : criterias

This commit is contained in:
Steve Ebersole 2012-07-16 20:45:09 -05:00
parent 219707df1d
commit f8d06216e5
10 changed files with 284 additions and 90 deletions

View File

@ -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',

View File

@ -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 @@ import org.hibernate.QueryException;
* @author Emmanuel Bernard
*/
public class QueryExecutionRequestException extends QueryException {
public QueryExecutionRequestException(String message, String queryString) {
super( message, queryString );
}

View File

@ -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 javax.persistence.metamodel.EntityType;
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 @@ import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
*
* @author Steve Ebersole
*/
public abstract class AbstractManipulationCriteriaQuery<T> implements CompilableCriteria {
public abstract class AbstractManipulationCriteriaQuery<T> implements CompilableCriteria, CommonAbstractCriteria {
private final CriteriaBuilderImpl criteriaBuilder;
private RootImpl<T> root;
@ -101,9 +101,7 @@ public abstract class AbstractManipulationCriteriaQuery<T> implements Compilable
}
public <U> Subquery<U> subquery(Class<U> 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<U>( criteriaBuilder(), type, this );
}

View File

@ -38,11 +38,11 @@ public class AbstractNode implements Serializable {
}
/**
* 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;
}
}

View File

@ -28,6 +28,7 @@ import java.util.List;
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.criteria.Subquery;
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 @@ import org.hibernate.jpa.criteria.path.RootImpl;
* @author Steve Ebersole
*/
public class CriteriaSubqueryImpl<T> extends ExpressionImpl<T> implements Subquery<T>, Serializable {
private final AbstractQuery<?> parent;
private final CommonAbstractCriteria parent;
private final QueryStructure<T> queryStructure;
public CriteriaSubqueryImpl(
CriteriaBuilderImpl criteriaBuilder,
Class<T> javaType,
AbstractQuery<?> parent) {
CommonAbstractCriteria parent) {
super( criteriaBuilder, javaType);
this.parent = parent;
this.queryStructure = new QueryStructure<T>( 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<T> getResultType() {
return getJavaType();
}
@ -88,23 +92,17 @@ public class CriteriaSubqueryImpl<T> extends ExpressionImpl<T> implements Subque
// ROOTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* {@inheritDoc}
*/
@Override
public Set<Root<?>> getRoots() {
return queryStructure.getRoots();
}
/**
* {@inheritDoc}
*/
@Override
public <X> Root<X> from(EntityType<X> entityType) {
return queryStructure.from( entityType );
}
/**
* {@inheritDoc}
*/
@Override
public <X> Root<X> from(Class<X> entityClass) {
return queryStructure.from( entityClass );
}
@ -112,45 +110,71 @@ public class CriteriaSubqueryImpl<T> extends ExpressionImpl<T> implements Subque
// SELECTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public Subquery<T> distinct(boolean applyDistinction) {
queryStructure.setDistinct( applyDistinction );
return this;
}
@Override
public boolean isDistinct() {
return queryStructure.isDistinct();
}
private Expression<T> wrappedSelection;
@Override
public Expression<T> getSelection() {
return (Expression<T>) queryStructure.getSelection();
if ( wrappedSelection == null ) {
if ( queryStructure.getSelection() == null ) {
return null;
}
wrappedSelection = new SubquerySelection<T>( (ExpressionImpl<T>) queryStructure.getSelection(), this );
}
return wrappedSelection;
}
@Override
public Subquery<T> select(Expression<T> expression) {
queryStructure.setSelection( expression );
return this;
}
public static class SubquerySelection<S> extends DelegatedExpressionImpl<S> {
private final CriteriaSubqueryImpl subQuery;
public SubquerySelection(ExpressionImpl<S> 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<T> where(Expression<Boolean> expression) {
queryStructure.setRestriction( criteriaBuilder().wrap( expression ) );
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Subquery<T> 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 class CriteriaSubqueryImpl<T> extends ExpressionImpl<T> implements Subque
// GROUPING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* {@inheritDoc}
*/
@Override
public List<Expression<?>> getGroupList() {
return queryStructure.getGroupings();
}
/**
* {@inheritDoc}
*/
@Override
public Subquery<T> groupBy(Expression<?>... groupings) {
queryStructure.setGroupings( groupings );
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Subquery<T> groupBy(List<Expression<?>> groupings) {
queryStructure.setGroupings( groupings );
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Predicate getGroupRestriction() {
return queryStructure.getHaving();
}
/**
* {@inheritDoc}
*/
@Override
public Subquery<T> having(Expression<Boolean> expression) {
queryStructure.setHaving( criteriaBuilder().wrap( expression ) );
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Subquery<T> having(Predicate... predicates) {
queryStructure.setHaving( criteriaBuilder().and( predicates ) );
return this;
@ -210,74 +222,62 @@ public class CriteriaSubqueryImpl<T> extends ExpressionImpl<T> implements Subque
// CORRELATIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* {@inheritDoc}
*/
public Set<Join<?, ?>> getCorrelatedJoins() {
@Override
public Set<Join<?, ?>> getCorrelatedJoins() {
return queryStructure.collectCorrelatedJoins();
}
/**
* {@inheritDoc}
*/
@Override
public <Y> Root<Y> correlate(Root<Y> source) {
final RootImpl<Y> correlation = ( ( RootImpl<Y> ) source ).correlateTo( this );
queryStructure.addCorrelationRoot( correlation );
return correlation;
}
/**
* {@inheritDoc}
*/
@Override
public <X, Y> Join<X, Y> correlate(Join<X, Y> source) {
final JoinImplementor<X,Y> correlation = ( (JoinImplementor<X,Y>) source ).correlateTo( this );
queryStructure.addCorrelationRoot( correlation );
return correlation;
}
/**
* {@inheritDoc}
*/
@Override
public <X, Y> CollectionJoin<X, Y> correlate(CollectionJoin<X, Y> source) {
final CollectionJoinImplementor<X,Y> correlation = ( (CollectionJoinImplementor<X,Y>) source ).correlateTo( this );
queryStructure.addCorrelationRoot( correlation );
return correlation;
}
/**
* {@inheritDoc}
*/
@Override
public <X, Y> SetJoin<X, Y> correlate(SetJoin<X, Y> source) {
final SetJoinImplementor<X,Y> correlation = ( (SetJoinImplementor<X,Y>) source ).correlateTo( this );
queryStructure.addCorrelationRoot( correlation );
return correlation;
}
/**
* {@inheritDoc}
*/
@Override
public <X, Y> ListJoin<X, Y> correlate(ListJoin<X, Y> source) {
final ListJoinImplementor<X,Y> correlation = ( (ListJoinImplementor<X,Y>) source ).correlateTo( this );
queryStructure.addCorrelationRoot( correlation );
return correlation;
}
/**
* {@inheritDoc}
*/
@Override
public <X, K, V> MapJoin<X, K, V> correlate(MapJoin<X, K, V> source) {
final MapJoinImplementor<X, K, V> correlation = ( (MapJoinImplementor<X, K, V>) source ).correlateTo( this );
queryStructure.addCorrelationRoot( correlation );
return correlation;
}
/**
* {@inheritDoc}
*/
@Override
public <U> Subquery<U> subquery(Class<U> 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 class CriteriaSubqueryImpl<T> extends ExpressionImpl<T> implements Subque
return subqueryBuffer.toString();
}
@Override
public String renderProjection(RenderingContext renderingContext) {
throw new IllegalStateException( "Subquery cannot occur in select clause" );
}

View File

@ -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
*/

View File

@ -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<T> extends ExpressionImpl<T> {
private final ExpressionImpl<T> wrapped;
public DelegatedExpressionImpl(ExpressionImpl<T> wrapped) {
super( wrapped.criteriaBuilder(), wrapped.getJavaType() );
this.wrapped = wrapped;
}
public ExpressionImpl<T> getWrapped() {
return wrapped;
}
// delegations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void registerParameters(ParameterRegistry registry) {
wrapped.registerParameters( registry );
}
@Override
public Selection<T> alias(String alias) {
wrapped.alias( alias );
return this;
}
@Override
public boolean isCompoundSelection() {
return wrapped.isCompoundSelection();
}
@Override
public List<ValueHandlerFactory.ValueHandler> getValueHandlers() {
return wrapped.getValueHandlers();
}
@Override
public List<Selection<?>> getCompoundSelectionItems() {
return wrapped.getCompoundSelectionItems();
}
@Override
public Class<T> getJavaType() {
return wrapped.getJavaType();
}
@Override
protected void resetJavaType(Class targetType) {
wrapped.resetJavaType( targetType );
}
@Override
protected void forceConversion(ValueHandlerFactory.ValueHandler<T> tValueHandler) {
wrapped.forceConversion( tValueHandler );
}
@Override
public ValueHandlerFactory.ValueHandler<T> getValueHandler() {
return wrapped.getValueHandler();
}
@Override
public String getAlias() {
return wrapped.getAlias();
}
@Override
protected void setAlias(String alias) {
wrapped.setAlias( alias );
}
}

View File

@ -30,6 +30,7 @@ import javax.persistence.PersistenceContextType;
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.Hibernate;
import org.hibernate.SessionFactory;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.ejb.AvailableSettings;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.ejb.HibernateQuery;
@ -205,11 +207,21 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
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;
}

View File

@ -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 class ManipulationCriteriaTest extends AbstractMetamodelSpecificTest {
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<Customer> criteria = builder.createCriteriaDelete( Customer.class );
Root<Customer> customerRoot = criteria.from( Customer.class );
Subquery<Double> subCriteria = criteria.subquery( Double.class );
Root<Customer> 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();
}
}

View File

@ -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 class UncorrelatedSubqueryTest extends AbstractMetamodelSpecificTest {
em.getTransaction().commit();
em.close();
}
@Test
public void testLessThan() {
CriteriaBuilder builder = entityManagerFactory().getCriteriaBuilder();
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Customer> criteria = builder.createQuery( Customer.class );
Root<Customer> customerRoot = criteria.from( Customer.class );
Subquery<Double> subCriteria = criteria.subquery( Double.class );
Root<Customer> 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();
}
}