Object vs. Entity
When working with JPA/Hibernate it is important to keep in mind that any object, even if it is an instance of a class annotated with @Entity
or mapped to an XML, not automatically a JPA entity.
This means that no matter if the PK is properly filled, an object is not a entity if you’re not managed in a persistence context (persistent context).
JPA states
Let’s look at a simplified JPA state diagram (source):
When we create an instance of the object it is in the state new (new). When we call the method persist
our object becomes a managed entity (Managed), although the record is probably not entered in the database until the commit
.
After the JPA context closes (commit
of the transaction, close
of EntityManager
or method detach
), our entity will enter the state Detached. Later we can bring it back to the JPA persistence context with the method merge
.
If we exclude an entity with the method remove
she will enter the state removed (re-moved) and until the end of the transaction a command DELETE
will be sent to the database. If we want to persist the entity again, we can call the method persist
.
How does the merge
Now that we understand the states of the entities, let us now see how the method works merge
. Here’s a very interesting diagram (source):
- The first step of
merge
is to verify that the entity is in the state managed (Managed), because if so the entity itself is returned.
- If the entity has been removed (re-moved) an error is thrown.
- If the persistence context already has a version of this entity, it will be updated with the state of the object passed by parameter and then will be returned.
- If there is no equivalent entity, it is verified that this is a new entity:
- If it is, a new instance is created (equivalent to
persist
).
- If nay is, the entity is loaded in the persistence context from the database.
A common error when using merge
Note that the main purpose of the method merge
is to return a JPA entity managed by the persistence context.
If the object passed by parameter is not a JPA entity, the same nay will be included in the persistence context, but a copy will be made and returned by the method merge
.
As described in this famous OS issue, a common error is to expect the object passed to the method to become an entity.
Incorrect
MyEntity e = new MyEntity();
em.merge(e);
e.setAtributo(novoValor);
In the above example, changing the value of the original object does not affect the state of the JPA entity created by merge
.
Correct
MyEntity e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setAtributo(novoValor);
Already in this example, the value will be updated in the database because the managed entity returned by merge
was modified.
Back to the question
With all the theory in mind we can conclude that the method merge
will include a new entity and, consequently, a new record in the database, when the object passed to the method is considered a new entity.
In what scenarios is this possible? I can’t predict them all, but here are the most common:
- PK is a sequential value (
@GeneratedValue
) and is not properly filled in, thus generating a new entry in the database.
- There’s a version column (
@Version
) which is not properly populated in the database (see this issue).
- There is a relationship with another entity and, for some reason, JPA is generating a query with
INNER JOIN
between the two tables and does not find results due to the absence of records in the other table, reaching the conclusion that the main entity does not exist. This article deals with the subject in relation to an Eclipselink annotation, but this can occur in Hibernate in some ways, for example via inheritance.
An alternative
Another way to use the merge
is to recover the managed entity and change it as needed.
Example:
MyEntity e = //entidade criada em algum outro lugar (JSF, Spring, etc.)
MyEntity e2 = em.find(MyEntity.class, idEntidade);
e.setAtributo(e.getAtributo);
While updating the attributes individually is more "laborious" and makes the code get bigger, this prevents the "magic" of frameworks or even the improper manipulation of attributes end up generating unexpected effects in the database.
The entity has set PK?
– Miguel Cartagena
Yes, I gave a syso before calling merge() and PK is != null
– Arthur Vinicius
My suspicion is that when I give Ubmit in my edit form it calls setId() somehow. As I did not put an inputText for my attribute id it arrow to 0. I say this because after Ubmit the id goes to 0. I am forgetting something ?
– Arthur Vinicius
As suspected, includes an inputHidden in the form with value of the id and gave normal update. This guy gave me the tip post
– Arthur Vinicius