Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make hibernate save() and update() as a single insert optimization

I'm wondering how to reduce SQL updates with my Hibernate (4) & Oracle setup.

A simple case might be this:

Open a session/transaction, create a new entity Xyz with session.save(), process some business logic, make some changes to Xyz and call session.update(), and let the session close as normal and Hibernate with commit to DB.

When committing, Hibernate will do an insert followed by an update -- but all it really needs to do is a single insert, but with the latest properties of Xyz, in this example.

Does anyone have any ideas/patterns for doing this sort of thing? Or is there any way to change Hibernate's behavior around this? Or what's your opinion on this -- is it a complete anti-pattern?

I know Hibernate is smart enough to omit multiple updates, when one update just overwrites another -- so why not similar for inserts?

This small snippet can reproduce the "issue":

MyEntity e = new MyEntity("xxx"); 
Session session = sessionFactory.openSession(); 
session.beginTransaction();
session.save(e); e.setName("yyy"); 
session.getTransaction().commit(); 
session.close();

By the way, I have no triggers to worry about.

This example is very simple and the performance problem might seem trivial, but in reality I am dealing with a more complex object (i.e., multiple inserts & updates), and high volumes, so avoiding updates would be great.

like image 565
ConorD55 Avatar asked Jun 05 '13 13:06

ConorD55


1 Answers

Is there a way I could add the object to the session, and it would be marked as persistent, but not call save() (that is, the INSERT will not be generated) until the very end?

Consider re-factoring to use Session.persist(Object) instead of Session.save(Object) if you want to defer the execution of INSERT until the session is flushed and/or the transaction is closed.

From section 10.2 of the JBoss Hibernate Community documentation:

You can also use persist() instead of save(), with the semantics defined in the EJB3 early draft.

  • persist() makes a transient instance persistent. However, it does not guarantee that the identifier value will be assigned to the persistent instance immediately, the assignment might happen at flush time. persist() also guarantees that it will not execute an INSERT statement if it is called outside of transaction boundaries. This is useful in long running conversations with an extended Session/persistence context.
  • save() does guarantee to return an identifier. If an INSERT has to be executed to get the identifier ( e.g. "identity" generator, not "sequence"), this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is problematic in a long-running conversation with an extended Session/persistence context.

Another useful reference can be found in the top-left cell of the method-to-scenario grid at the top of the "Putting it All Together" section of this blog article that describes the behavior of persist() on objects that have never been persisted as follows:

  1. Object added to persistence context as new entity
  2. New entity inserted into database at flush()/commit()
like image 100
John Heinnickel Avatar answered Oct 02 '22 16:10

John Heinnickel