jsr338-experts@jpa-spec.java.net

[jsr338-experts] criteria API bulk update/delete

From: Linda DeMichiel <linda.demichiel_at_oracle.com>
Date: Mon, 28 Mar 2011 11:53:12 -0700

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();
}