Criteria Update/Delete – The easy way to implement bulk operations with JPA2.1
Take your skills to the next level!
The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.
JPA 2.1 added a list of nice features to the specification. One of them is the support for bulk update and delete operations in the Criteria API. We will have a look at the new CriteriaUpdate and CriteriaDelete classes in this article.
If you like to learn more about the other features added in JPA 2.1, have a look at this overview or download the free New Features in JPA 2.1 cheat sheet.
CriteriaUpdate
The CriteriaUpdate interface can be used to implement bulk update operations. But be careful, these operations are directly mapped to database update operations. Therefore the persistence context is not synchronized with the result and there is no optimistic locking of the involved entities. If you use optimistic locking, you need to update the version column as part of your update statement.
The following code shows a bulk update of the amount of multiple orders.
@Stateless @LocalBean public class OrderManagement { @PersistenceContext private EntityManager em; ... public void updateOrder(Double oldAmount, Double newAmount) { CriteriaBuilder cb = this.em.getCriteriaBuilder(); // create update CriteriaUpdate<Order> update = cb. createCriteriaUpdate(Order.class); // set the root class Root e = update.from(Order.class); // set update and where clause update.set("amount", newAmount); update.where(cb.greaterThanOrEqualTo(e.get("amount"), oldAmount)); // perform update this.em.createQuery(update).executeUpdate(); } ... }
As you can see, the implementation is very similar to implementing a CriteriaQuery known from JPA2.0. The main differences are the usage of the CriteriaUpdate interface and the call of the update specific set method.
CriteriaDelete
The usage of the CriteriaDelete interface for bulk delete operations is nearly the same as of the CriteriaUpdate interface. Like the CriteriaUpdate operations, the CriteriaDelete operations are directly mapped to database delete operations. Therefore the persistence context is not synchronized with the result of this operation.
The example of a CriteriaDelete operation looks similar to the usage of CriteriaQuery known from JPA2.0 and the CriteriaUpdate operation described above:
@Stateless @LocalBean public class OrderManagement { @PersistenceContext private EntityManager em; ... public void deleteOrder(Double amount) { CriteriaBuilder cb = this.em.getCriteriaBuilder(); // create delete CriteriaDelete<Order> delete = cb. createCriteriaDelete(Order.class); // set the root class Root e = delete.from(Order.class); // set where clause delete.where(cb.lessThanOrEqualTo(e.get("amount"), amount)); // perform update this.em.createQuery(delete).executeUpdate(); } }
OK, I don’t think this needs any explanation …
Conclusion
The new CriteriaUpdate and CriteriaDelete interfaces add the missing bulk update and delete operations to the Criteria API. From my point of view this is a small but great enhancement that allows us to use the Criteria API in even more situations. I like it!
If you want to try it yourself, you can use any JPA 2.1 implementation like Hibernate or EclipseLink . You can find the source code of the examples in my github repo.
And don’t forget to download your New Features in JPA 2.1 cheat sheet.
And if you like to read more about the new JPA 2.1 features, make sure to have a look at my other articles …
I have a question for you.
what happen if I have million of records will affeted.
can the CriteriaUpdate handle millions of the records
JPA/Hibernate just create an SQL UPDATE statement and send it to the database. So, from their point of view, it doesn’t matter how many records are affected.
The only question is if your database can handle the update. That’s impossible to answer without knowing your DBMS, the table structure, the update statement and the number of affected records. In general, I wouldn’t expect any issues. Databases handle these kinds of operations very efficiently. If you’re worried about it, please ask your DBA to take a look at it.
Thank you for your information about sing Criteria with Update and Delete ;=)
Hi Thorben, since these operations are not synced with the persistence context does that mean that they will also not synchronise with Hibernate Search also? ie if I used criteriaDelete to bulk delete records from the db would I manually have to delete them from the lucene index too?
Hi Martin,
so far, I didn’t use these updates/deletes together with Hibernate Search. But I would expect that you have to update the index manually.
Hibernate doesn’t know which entities were changed/deleted and I don’t see how it should update the index.
But as I said, I haven’t tried it.
Regards,
Thorben
First of all thank you for posting this website. It is amazing that I can have so many answers to my questions here.
Can you please provide the same example, but using metamodel?
Thanks.
First of all thank you for posting this website. It is amazing that I can have so many answers to my questions here.
Can you please provide the same example, but using metamodel?
Thanks.
I tried to use this code, but update is not of type CriteriaQuery. It is of type Criteriaupdate. As a result when I try to write this.em.creteQuery (update) as you suggest the IDE forces me to cast it to CriteriaQuery.
Have not check that it works yet but is this correct?
Hi,
the JPA 2.1 EntityManager provides different variations of the createQuery method. One of them expects a CriteriaUpdate object: https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/EntityManager.html#createQuery(javax.persistence.criteria.CriteriaUpdate)
and another one a CriteriaDelete object: https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/EntityManager.html#createQuery(javax.persistence.criteria.CriteriaDelete)
So a cast to CriteriaQuery is not required.
Regards,
Thorben
Hi,
using the JPA metamodel is quite simple. You just need to create the metamodel classes and replace “amount” with Order_.amount.
I’ll publish a more detailed post about it next week.
Regards,
Thorben