jsr338-experts@jpa-spec.java.net

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

From: Linda DeMichiel <linda.demichiel_at_oracle.com>
Date: Fri, 10 Feb 2012 13:46:33 -0800

Please see below...

On 4/28/2011 1:10 PM, Linda DeMichiel wrote:
>
>
> On 4/28/2011 4:04 AM, Emmanuel Bernard wrote:
>> 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).
>>
>
> Yes -- I think this would send a clearer message.
>
>> We might be able to override from() in CriteriaDelete and CriteriaUpdate subclasses if you really want to bind them to
>> a type
>>

To return to this specific point. We took this path back in April (and it is reflected in
the current javadocs). However, while this worked when I tested it back then (jdk 1.6.0_25),
it now results in compiler errors. The obvious fix is to move the from() methods in CommonAbstractQuery
back down into AbstractQuery. At this point then, I think that CommonAbstractQuery serves
little purpose, and my plan is to refactor to remove it. There should otherwise be no user-visible
changes.

-Linda


>
> That would make more sense with regard to the typing of the interfaces. The other
> consequence would be to enforce a root over just that type.
>
> -Linda
>
>> 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();
>>> }
>>
>