An issue is raised when the return value of an entity merge operation is discarded.

Why is this an issue?

In ORM (Object-Relational Mapping) frameworks, entities can be in different states within the tracking context:

State merging operations are used to copy the state of a detached or transient entity into a tracked entity within the current ORM session. Importantly, ORM specifications typically state that:

If you discard the return value and continue using the original entity instance, any modifications you make will not be tracked by the ORM session. These changes will not be synchronized to the database during the transaction commit, leading to data loss.

The only safe way to use state merging operations is to capture the return value and use that tracked instance for all subsequent operations.

In JPA specifically, this refers to the EntityManager.merge() method operating on the persistence context. The entity states are called managed (rather than tracked), and the association is with a persistence context (rather than an ORM session).

What is the potential impact?

Data loss can occur when changes made to the entity are not persisted to the database. This can lead to:

In business-critical applications, this can result in lost customer data, incorrect financial records, or corrupted application state.

How to fix it

Capture the return value of merge() and use the returned managed entity for all subsequent operations. Stop using the original detached entity after the merge.

Code examples

Noncompliant code example

public void updateEntity(MyEntity detachedEntity) {
    EntityManager em = getEntityManager();
    em.merge(detachedEntity); // Noncompliant; changes are NOT persisted
    detachedEntity.setName("Updated Name");
    detachedEntity.setValue(42);
}

Compliant solution

public void updateEntity(MyEntity detachedEntity) {
    EntityManager em = getEntityManager();
    MyEntity managedEntity = em.merge(detachedEntity);  // Compliant; changes ARE persisted
    managedEntity.setName("Updated Name");
    managedEntity.setValue(42);
}

Resources

Documentation