tags:

views:

38

answers:

1

Hi all I'm just geting to grips with hibernate. Little bit confused.

I just wanted to watch the operation of the first level cache, which I understood to batch up queries until the end of the session.

But if I create an object, hibernate saves it immediately, so that when I later update it in the same transaction, it has to do an update too:

Session session = factory.getCurrentSession();
session.beginTransaction();

Test1 test1 = new Test1();
test1.setName("Test 1");
test1.setValue(10);
// Touch it
session.save(test1);

System.out.println("At checkpoint 1");

test1.setValue(20);

session.getTransaction().commit();

I see the sql for the save, then 'At checkpoint 1', then the sql for the update. Do I have something set up wrong or am I misunderstanding hibernate's first level cache? Is there a good document on the first level cache - I didn't find anything in the hibernate docs, but I could easily have missed it..

Thanks!

+1  A: 

For the first version of your question, you'll find some answers in the chapter 10.2. Making objects persistent of the documentation:

Newly instantiated instances of a a persistent class are considered transient by Hibernate. We can make a transient instance persistent by associating it with a session:

DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
Long generatedId = (Long) sess.save(fritz);

If Cat has a generated identifier, the identifier is generated and assigned to the cat when save() is called. If Cat has an assigned identifier, or a composite key, the identifier should be assigned to the cat instance before calling save(). 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.

BTW, if you look closely at the save() method, you'll notice that it indeed returns Serializable (the assigned identifier).


Now, regarding the updated version of your question, Hibernates uses an ActionQueue that holds the DML operations queued as part of a session's transactional-write-behind semantics. DML operations are queued here until a flush forces them to be executed against the database.

When you call save() or persist(), an insert action that contains a copy of the values of the entity to be inserted is added to the "insertions" list.

Then, you modify the persistent entity and, at flush time, the entity is detected as dirty and another action (an update action) is added for that same entity with another copy of that entity's values: the pending insert action is not updated with the new values.

And this results in two DML operations (an INSERT and an UPDATE).

This behavior is somehow explained in the comments of HHH-2588. It is (certainly) not optimal but this is how the current Hibernate implementation does work and I am not aware of all the details to explain why Hibernate doesn't perform this optimization (I guess it's not that simple). But feel free to submit a patch :)

Pascal Thivent
Pascal, thank you so much for your full and clear answer. I don't know where you find the patience for such noob questions, but I am very grateful for it :-)
Jeremy
@Bruce You're welcome. Actually, it's far from a noob question IMO, I find it pretty interesting (and many people don't even try to understand things like you currently do).
Pascal Thivent