EntityManager.merge()
can insert new objects and update existing ones.
Why would one want to use persist()
(which can only create new objects)?
EntityManager.merge()
can insert new objects and update existing ones.
Why would one want to use persist()
(which can only create new objects)?
I was getting lazyLoading exceptions on my entity because I was trying to access a lazy loaded collection that was in session.
What I would do was in a separate request, retrieve the entity from session and then try to access a collection in my jsp page which was problematic.
To alleviate this, I updated the same entity in my controller and passed it to my jsp, although I imagine when I re-saved in session that it will also be accessible though
SessionScope
and not throw aLazyLoadingException
, a modification of example 2:The following has worked for me:
If you're using the assigned generator, using merge instead of persist can cause a redundant SQL statement, therefore affecting performance.
Also, calling merge for managed entities is also a mistake since managed entities are automatically managed by Hibernate and their state is synchronized with the database record by the dirty checking mechanism upon flushing the Persistence Context.
To understand how all this works, you should first know that Hibernate shifts the developer mindset from SQL statements to entity state transitions.
Once an entity is actively managed by Hibernate, all changes are going to be automatically propagated to the database.
Hibernate monitors currently attached entities. But for an entity to become managed, it must be in the right entity state.
First, we must define all entity states:
New (Transient)
A newly created object that hasn’t ever been associated with a Hibernate
Session
(a.k.aPersistence Context
) and is not mapped to any database table row is considered to be in the New (Transient) state.To become persisted we need to either explicitly call the
EntityManager#persist
method or make use of the transitive persistence mechanism.Persistent (Managed)
A persistent entity has been associated with a database table row and it’s being managed by the current running Persistence Context. Any change made to such entity is going to be detected and propagated to the database (during the Session flush-time). With Hibernate, we no longer have to execute INSERT/UPDATE/DELETE statements. Hibernate employs a transactional write-behind working style and changes are synchronized at the very last responsible moment, during the current
Session
flush-time.Detached
Once the current running Persistence Context is closed all the previously managed entities become detached. Successive changes will no longer be tracked and no automatic database synchronization is going to happen.
To associate a detached entity to an active Hibernate Session, you can choose one of the following options:
Reattaching
Hibernate (but not JPA 2.1) supports reattaching through the Session#update method. A Hibernate Session can only associate one Entity object for a given database row. This is because the Persistence Context acts as an in-memory cache (first level cache) and only one value (entity) is associated to a given key (entity type and database identifier). An entity can be reattached only if there is no other JVM object (matching the same database row) already associated to the current Hibernate Session.
Merging
The merge is going to copy the detached entity state (source) to a managed entity instance (destination). If the merging entity has no equivalent in the current Session, one will be fetched from the database. The detached object instance will continue to remain detached even after the merge operation.
Removed
Although JPA demands that managed entities only are allowed to be removed, Hibernate can also delete detached entities (but only through a Session#delete method call). A removed entity is only scheduled for deletion and the actual database DELETE statement will be executed during Session flush-time.
To understand the JPA state transitions better, you can visualize the following diagram:
Or if you use the Hibernate specific API:
Another observation:
merge()
will only care about an auto-generated id(tested onIDENTITY
andSEQUENCE
) when a record with such an id already exists in your table. In that casemerge()
will try to update the record. If, however, an id is absent or is not matching any existing records,merge()
will completely ignore it and ask a db to allocate a new one. This is sometimes a source of a lot of bugs. Do not usemerge()
to force an id for a new record.persist()
on the other hand will never let you even pass an id to it. It will fail immediately. In my case, it's:hibernate-jpa javadoc has a hint:
Persisting entities
In contrast to the merge method the persist method is pretty straightforward and intuitive. The most common scenario of the persist method's usage can be summed up as follows:
"A newly created instance of the entity class is passed to the persist method. After this method returns, the entity is managed and planned for insertion into the database. It may happen at or before the transaction commits or when the flush method is called. If the entity references another entity through a relationship marked with the PERSIST cascade strategy this procedure is applied to it also."
The specification goes more into details, however, remembering them is not crucial as these details cover more or less exotic situations only.
Merging entities
In comparison to persist, the description of the merge's behavior is not so simple. There is no main scenario, as it is in the case of persist, and a programmer must remember all scenarios in order to write a correct code. It seems to me that the JPA designers wanted to have some method whose primary concern would be handling detached entities (as the opposite to the persist method that deals with newly created entities primarily.) The merge method's major task is to transfer the state from an unmanaged entity (passed as the argument) to its managed counterpart within the persistence context. This task, however, divides further into several scenarios which worsen the intelligibility of the overall method's behavior.
Instead of repeating paragraphs from the JPA specification I have prepared a flow diagram that schematically depicts the behaviour of the merge method:
So, when should I use persist and when merge?
persist
merge
Persist and merge are for two different purposes (they aren't alternatives at all).
(edited to expand differences information)
persist:
merge:
persist() efficiency:
persist() semantics:
Example:
This way only exists 1 attached object for any register in the entity manager.
merge() for an entity with an id is something like:
Although if connected to MySQL merge() could be as efficient as persist() using a call to INSERT with ON DUPLICATE KEY UPDATE option, JPA is a very high level programming and you can't assume this is going to be the case everywhere.
I found this explanation from the Hibernate docs enlightening, because they contain a use case:
From: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html