12

I found that JPA does not support the following Update:

Update Person p set p.name = :name_1 where p.id = :id_1,
                    p.name = :name_2 where p.id = :id_2,
                    p.name = :name_3 where p.id = :id_3
                    .... 
                 // It could go on, depending on the size of the input. Could be in 100s

So I have two options:

Option 1:

Query q = em.createQuery("Update Person p set p.name = :name where p.id = :id");

For ( int x=0; PersonsList.length; x++ ) {
      // add name and id parameters
      em.executeUpdate(); 
}

Questions:

  1. Is this all that's needed for Batch update? Anything else I need to add? I set hibernate.jdbc.batch_size", "20"
  2. Is the optimistic lock enabled here by default? (I do not have @Version in my entity though)
  3. What do I need to do in order to enforce Optimistic Locking, if not @Version?

Option 2:

Construct one single query using either Select Case syntax or with Criteria API

Questions:

  1. Does the batching still happen here? (In a single big query)
  2. Is this better than the 1st approach in terms of performance?
  3. Whats the recommended approach out of these two options? Any other better approach?
Ahmad Hosny
  • 597
  • 1
  • 6
  • 23
Kevin Rave
  • 13,876
  • 35
  • 109
  • 173
  • C'mon do you really want to run a bulk for 3 rows? bulk causes an Intent lock on target table and it's not good,, just start the batch by `em.begin()` and call every DMLs and finally 'em.commit();' –  Jul 11 '13 at 20:09
  • No. Its dynamic. I just showed 3 in example :-). It could be 100s. – Kevin Rave Jul 11 '13 at 20:13
  • it dependents buddy, what kind of DB do you use? and how often do you want to run such this query(s)? but if you ask me I prefer DB prefer way, and run it parallel with low priority –  Jul 11 '13 at 20:23
  • We use Oracle. Its a Webservice call. – Kevin Rave Jul 11 '13 at 20:42
  • if the process disturb your system just go for native Oracle 'bulk collect and forall', else option one with transaction would help. also I suggest you split the target table(s) by each bulk operation, then it ensures you BX will just hold the desired section, not whole of the table, as I said it dependents dude. –  Jul 11 '13 at 20:56
  • My question is: With option 1, does it "automatically batch" the updates? Or I need to do some config or anything as such? – Kevin Rave Jul 11 '13 at 21:24
  • 1
    Well yes buddy, it will set the batch size automatically and as you set it as 20, you will have 20 calls for each batch, but it's not recommended for heavy loads, usually `batch_size` value is between 10 and 35 (dependents), for very heavy loads use db prefer way :) –  Jul 13 '13 at 03:56

3 Answers3

6

In your question title, you mentioned Bulk Update and Delete, but you actually need batching this time.

Bulk Update and Delete are needed when you want to UPDATE/DELETE rows that all match the same filtering criteria which can be expressed in the WHERE clause.

Here, you need JDBC batch updates. You need to set the following configuration property:

<property name="hibernate.jdbc.batch_size" value="50"/>

If you do this, you can simply update the entities, and Hibernate will batch the UPDATE statements for you.

Option 1 is not very useful because it will generate N UPDATE statements that cannot be batched.

Option 2 is not very useful either because it will generate a very complex query, whose Execution Plan is probably more complex than executing everything in a simple batched UPDATE statement.

So, do it like this:

  1. Fetch entities using pagination
  2. Update them using Hibernate and let it do the batch update for you.

If you have many such entities, use query pagination.

When using bulk updates, Hibernate does not increment the version column used for optimistic locking automatically, as it does for regular entity updates, so you need to increment the version entity attribute explicitly.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
5

You can update a list of object without iterate over all the collection.

List<Integer> demo = Arrays.asList(1,2,3,4);
final String update = "UPDATE User u SET u.enabled = true WHERE u.id IN (?1)";

return super.getEntityManager()
    .createQuery(update)
    .setParameter(1, ids)
    .executeUpdate();
OJVM
  • 1,403
  • 1
  • 25
  • 37
  • 1
    How can we update say u.enabled = true for few Ids and u.enabled = false for other few Ids? – PAA Oct 06 '21 at 14:42
  • i think the best is to make 2 updates. avoiding complex solutions. – OJVM Oct 06 '21 at 17:00
  • 1
    What is super.getEntityManager()? Is it different than EntityManager class object from PersistantContext? – user20072008 May 19 '22 at 23:17
  • Hi, in this case, super is used because it comes from an inherited class, which has the following code `private EntityManager entityManager;` `public EntityManager getEntityManager(){ return this.entityManager; }` – OJVM May 24 '22 at 19:16
2

If you are going to use batch see this chapter of the hibernate documentation

The batch_size is more to memory optimization than query optimization, the query is remain the same, but you can also minimize the roundtrip by the full use of your memory. you need to flush() and clear() each N times depend of the setting of batch_size.

but still . .

Update in 1 statement is much faster than update in multiple statement, so if you :

  • Can simply loop and do that in SQL
  • No need for cascade that update to other entities
  • You really need much faster performance when updating multiple value
  • And no need other HQL advantage like prepared statement that prevent sql injection

Then you can consider to just create native query than hql.

Angga
  • 2,305
  • 1
  • 17
  • 21
  • But, if I use update native query, would I be not getting "Batch Update" mode? I was told to use Batch Update. So I am wondering what is the best way to do it – Kevin Rave Jul 26 '13 at 17:04
  • as i said batch_size is just a matter of memory management, so you can commit ten of thousands row in one commit without having a **OutOfMemoryException**. so if your native query can do what you need without having OutOfMemoryException, you can consider using it, but if not you should use HQL instead. But i thing SQL will not having OutOfMemoryException problem because hibernate just save **persisted** objects to the session-level cache. – Angga Jul 27 '13 at 10:58
  • @Angga Native queries under `EntityManager` do not suport batching. You have to get the connection handle and then use `PreparedStatement` in order to use the batch API. – TheRealChx101 Aug 23 '22 at 07:35