jsr338-experts@jpa-spec.java.net

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

From: Linda DeMichiel <linda.demichiel_at_oracle.com>
Date: Thu, 28 Apr 2011 13:10:53 -0700

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
>

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