jsr338-experts@jpa-spec.java.net

[jsr338-experts] Re: criteria API bulk update/delete

From: Emmanuel Bernard <emmanuel.bernard_at_jboss.com>
Date: Thu, 28 Apr 2011 07:04:54 -0400

I haven't fired up my compiler nor spent too much time on it but could we get rid of the T param in CommonAbstractQuery (now CriteriaBase)? I can't seem to find what type-safety purpose it plays.
What I do not like is that T in CommonAbstractQuery and T in AbstractQuery have different meanings (which smells like something we will regret later on).

We might be able to override from() in CriteriaDelete and CriteriaUpdate subclasses if you really want to bind them to a type

class CriteriaDelete<T> extends CriteriaBase {
  Root<T> from(Class<T> entityClass);
}

Otherwise name wise, CriteriaBase, AbstractQuery (existing) CriteriaUpdate CriteriaDelete seem quite natural to me.

Emmanuel

On 28 mars 2011, at 20:53, Linda DeMichiel wrote:

> One of the features of JPQL that the criteria API lacks is the
> ability to issue bulk updates and deletes.
>
> Here's a cut at a strawman API for update and delete criteria queries.
> There are a number of issues here that are in need of feedback.
>
> There are 3 new interfaces, which I've attached to the message:
> UpdateQuery
> DeleteQuery
> CommonAbstractQuery
>
> I spent a long time mulling over whether to introduce a new type to
> model the target (i.e. entity/table) of the update operation because
> of the restricted semantics of bulk operation targets in SQL (i.e.,
> single table, no derived joins in the UPDATE clause) or whether to
> reuse Root. In the end, I decided to reuse Root, because of the
> extensions that many databases provide to support joins. In this
> case, the joined item is not itself updatable, but is used in the
> expression of constraints or as a source of new values. However, we
> will need to decide exactly what we state in terms of what is
> "defined" vs "undefined" in the spec. Probably the most conservative
> statement would be to state that the use of more than one root and/or
> the use of joins is undefined.
>
> Another issue is whether UpdateQuery and DeleteQuery should exist in
> any form of inheritance hierarchy to allow common information to be
> factored out. I've done this in the interfaces attached to this
> message. (The root of the factored hierarchy could certainly use a
> better name, however.)
>
>
> The CriteriaBuilder interface would be extended with methods
> like:
>
> <T> UpdateQuery<T> createUpdateQuery(Class<T> targetEntity);
> <T> UpdateQuery<T> createUpdateQuery(EntityType<T> targetEntity);
> <T> DeleteQuery<T> createDeleteQuery(Class<T> targetEntity);
> <T> DeleteQuery<T> createDeleteQuery(EntityType<T> targetEntity);
>
> And the EntityManager interface would be extended with methods like:
>
> public <T> TypedQuery createQuery(UpdateQuery<T> updateQuery)
> public <T> TypedQuery createQuery(DeleteQuery<T> deleteQuery)
>
>
> Example queries:
>
> // Simple Update
>
> UpdateQuery<Customer> q1 = cb.createUpdateQuery(Customer.class);
> Root<Customer> c1 = q1.from(Customer.class);
> q1.set(c1.get(Customer_.firstName), "Fred")
> .set(c1.get(Customer_.lastName), "Jones")
> .where(cb.equal(c1.get(Customer_.id), 1));
>
>
> // Simple Delete
>
> DeleteQuery<Customer> q2 = cb.createDeleteQuery(Customer.class);
> Root<Customer> c2 = q2.from(Customer.class);
> q2.where(cb.equal(c2.get(Customer_.id), 5));
>
>
> // Bulk Update
>
> UpdateQuery<Employee> q3 = cb.createUpdateQuery(Employee.class);
> Root<Employee> e3 = q3.from(Employee.class);
> q3.set(e3.get(Employee_.salary),
> cb.prod(e3.get(Employee_.salary), 1.1).as(BigDecimal.class))
> .where(cb.equal(e3.get(Employee_.dept).get(Department_.name), "Sales"));
>
>
> // Bulk Delete
>
> DeleteQuery<Employee> q4 = cb.createDeleteQuery(Employee.class);
> Root<Employee> e4 = q4.from(Employee.class);
> q4.where(cb.equal(e4.type(), Exempt.class));
>
>
> //Update query with multiple levels
>
> UpdateQuery<Customer> q5 = cb.createUpdateQuery(Customer.class);
> Root<Customer> c5 = q5.from(Customer.class);
> q5.set(c5.get(Customer_.address).get(Address_.city), "NewYork")
> .set(c5.get(Customer_.address).get(Address_.state), "NewYork")
> .where(cb.equal(c5.get(Customer_.id), 1));
>
>
> Opinions, please.....
>
> thanks,
>
> -Linda
> package javax.persistence.criteria;
>
> import javax.persistence.metamodel.SingularAttribute;
>
> public interface UpdateQuery<T> extends CommonAbstractQuery<T> {
>
> Root<T> getRoot();
>
> <Y> UpdateQuery<T> set(SingularAttribute<? super T, Y> attribute, Y value);
>
> <Y> UpdateQuery<T> set(SingularAttribute<? super T, Y> attribute,
> Expression<? extends Y> value);
>
> <Y> UpdateQuery<T> set(Path<Y> attribute, Y value);
>
> <Y> UpdateQuery<T> set(Path<Y> attribute, Expression<? extends Y> value);
>
> UpdateQuery<T> where(Expression<Boolean> restriction);
>
> UpdateQuery<T> where(Predicate... restrictions);
>
> }
> package javax.persistence.criteria;
>
> public interface DeleteQuery<T> extends CommonAbstractQuery<T> {
>
> Root<T> getRoot();
>
> DeleteQuery<T> where(Expression<Boolean> restriction);
>
> DeleteQuery<T> where(Predicate... restrictions);
>
> }
>
> package javax.persistence.criteria;
>
> import java.util.List;
> import java.util.Set;
>
> import javax.persistence.metamodel.EntityType;
>
> /**
> * The <code>CommonAbstractQuery</code> interface defines functionality
> * that is common to update and delete criteria operations ("update
> * queries" and "delete queries" and to both top-level queries and
> * subqueries.
> * It is not intended to be used directly in query construction.
> *
> * <p> All queries must have:
> * a set of root entities
> * <p> All queries may have:
> * a conjunction of restrictions.
> *
> * @param <T> the type of the query
> *
> * @since Java Persistence 2.1
> */
> public interface CommonAbstractQuery<T> {
>
> /**
> * Create and add a query root corresponding to the given entity,
> * forming a cartesian product with any existing roots.
> * @param entityClass the entity class
> * @return query root corresponding to the given entity
> */
> <X> Root<X> from(Class<X> entityClass);
>
> /**
> * Create and add a query root corresponding to the given entity,
> * forming a cartesian product with any existing roots.
> * @param entity metamodel entity representing the entity
> * of type X
> * @return query root corresponding to the given entity
> */
> <X> Root<X> from(EntityType<X> entity);
>
> /**
> * Modify the query to restrict the query results according
> * to the specified boolean expression.
> * Replaces the previously added restriction(s), if any.
> * @param restriction a simple or compound boolean expression
> * @return the modified query
> */
> CommonAbstractQuery<T> where(Expression<Boolean> restriction);
>
> /**
> * Modify the query to restrict the query results according
> * to the conjunction of the specified restriction predicates.
> * Replaces the previously added restriction(s), if any.
> * If no restrictions are specified, any previously added
> * restrictions are simply removed.
> * @param restrictions zero or more restriction predicates
> * @return the modified query
> */
> CommonAbstractQuery<T> where(Predicate... restrictions);
>
> /**
> * Create a subquery of the query.
> * @param type the subquery result type
> * @return subquery
> */
> <U> Subquery<U> subquery(Class<U> type);
>
> /**
> * Return the predicate that corresponds to the where clause
> * restriction(s), or null if no restrictions have been
> * specified.
> * @return where clause predicate
> */
> Predicate getRestriction();
>
>
> }
>
> package javax.persistence.criteria;
>
> import java.util.List;
> import java.util.Set;
>
> import javax.persistence.metamodel.EntityType;
>
> /**
> * The <code>AbstractQuery</code> interface defines functionality that is common
> * to both top-level queries and subqueries.
> * It is not intended to be used directly in query construction.
> *
> * <p> All queries must have:
> * a set of root entities (which may in turn own joins).
> * <p> All queries may have:
> * a conjunction of restrictions.
> *
> * @param <T> the type of the result
> *
> * @since Java Persistence 2.0
> */
> public interface AbstractQuery<T> extends CommonAbstractQuery<T> {
>
> /**
> * Modify the query to restrict the query results according
> * to the specified boolean expression.
> * Replaces the previously added restriction(s), if any.
> * @param restriction a simple or compound boolean expression
> * @return the modified query
> */
> AbstractQuery<T> where(Expression<Boolean> restriction);
>
> /**
> * Modify the query to restrict the query results according
> * to the conjunction of the specified restriction predicates.
> * Replaces the previously added restriction(s), if any.
> * If no restrictions are specified, any previously added
> * restrictions are simply removed.
> * @param restrictions zero or more restriction predicates
> * @return the modified query
> */
> AbstractQuery<T> where(Predicate... restrictions);
>
> /**
> * Specify the expressions that are used to form groups over
> * the query results.
> * Replaces the previous specified grouping expressions, if any.
> * If no grouping expressions are specified, any previously
> * added grouping expressions are simply removed.
> * @param grouping zero or more grouping expressions
> * @return the modified query
> */
> AbstractQuery<T> groupBy(Expression<?>... grouping);
>
> /**
> * Specify the expressions that are used to form groups over
> * the query results.
> * Replaces the previous specified grouping expressions, if any.
> * If no grouping expressions are specified, any previously
> * added grouping expressions are simply removed.
> * @param grouping list of zero or more grouping expressions
> * @return the modified query
> */
> AbstractQuery<T> groupBy(List<Expression<?>> grouping);
>
> /**
> * Specify a restriction over the groups of the query.
> * Replaces the previous having restriction(s), if any.
> * @param restriction a simple or compound boolean expression
> * @return the modified query
> */
> AbstractQuery<T> having(Expression<Boolean> restriction);
>
> /**
> * Specify restrictions over the groups of the query
> * according the conjunction of the specified restriction
> * predicates.
> * Replaces the previously having added restriction(s), if any.
> * If no restrictions are specified, any previously added
> * restrictions are simply removed.
> * @param restrictions zero or more restriction predicates
> * @return the modified query
> */
> AbstractQuery<T> having(Predicate... restrictions);
>
> /**
> * Specify whether duplicate query results will be eliminated.
> * A true value will cause duplicates to be eliminated.
> * A false value will cause duplicates to be retained.
> * If distinct has not been specified, duplicate results must
> * be retained.
> * @param distinct boolean value specifying whether duplicate
> * results must be eliminated from the query result or
> * whether they must be retained
> * @return the modified query
> */
> AbstractQuery<T> distinct(boolean distinct);
>
> /**
> * Return the query roots. These are the roots that have
> * been defined for the <code>CriteriaQuery</code> or <code>Subquery</code> itself,
> * including any subquery roots defined as a result of
> * correlation. Returns empty set if no roots have been defined.
> * Modifications to the set do not affect the query.
> * @return the set of query roots
> */
> Set<Root<?>> getRoots();
>
> /**
> * Return the selection of the query, or null if no selection
> * has been set.
> * @return selection item
> */
> Selection<T> getSelection();
>
> /**
> * Return a list of the grouping expressions. Returns empty
> * list if no grouping expressions have been specified.
> * Modifications to the list do not affect the query.
> * @return the list of grouping expressions
> */
> List<Expression<?>> getGroupList();
>
> /**
> * Return the predicate that corresponds to the restriction(s)
> * over the grouping items, or null if no restrictions have
> * been specified.
> * @return having clause predicate
> */
> Predicate getGroupRestriction();
>
> /**
> * Return whether duplicate query results must be eliminated or
> * retained.
> * @return boolean indicating whether duplicate query results
> * must be eliminated
> */
> boolean isDistinct();
>
> /**
> * Return the result type of the query or subquery. If a result
> * type was specified as an argument to the
> * <code>createQuery</code> or <code>subquery</code> method, that
> * type will be returned. If the query was created using the
> * <code>createTupleQuery</code> method, the result type is
> * <code>Tuple</code>. Otherwise, the result type is
> * <code>Object</code>.
> * @return result type
> */
> Class<T> getResultType();
> }