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 :)