Question

I have a method that receives a JPA Entityand its related EntityManager as parameters. The Entity instance is not created inside the class, and it might very well be shared by other classes (like GUIs and such).

The method starts a transaction, carries out some changes on the entity, and finally commits the transaction.

In case the commit fails, EntityTransaction.rollback() is called: in accordance with JPA specifications, the entity is then detached from the manager.

In case of failure the application needs to discard the pending changes, restore the original values inside the entity e and re-attach it to the EntityManager, so that the various scattered references to the e object would remain valid. The problem raises here: what I understood is that this is not a straightforward operation using the EntityManager's APIs:

  • calling EntityManager.refresh(e) is not possible since e is detached.
  • doing e = EntityManager.merge(e) would create a new instance for e: all the other references to the original e in the program at runtime would not be updated to the new instance. This is the main issue.
  • moreover (actually not quite sure about this), EntityManager.merge(e) would update the new managed instance's values with the values currently held by e (i.e., the values that probably caused the commit to fail). Instead, what I need is to reset them.

Sample code:

public void method(EntityManager em, Entity e) {
    EntityTransaction et = em.getTransaction();
    et.begin();
    ...
    // apply some modifications to the entity's fields
    ...
    try {
        et.commit();
    } catch (Exception e) {
        et.rollback();

        // now that 'e' is detached from the EntityManager, how can I:
        // - refresh 'e', discarding all pending changes
        // - without instantiating a copy (i.e. without using merge())
        // - reattach it
    }
}

What is the best approach in this case?

Was it helpful?

Solution

A possible solution would be like:

public class YourClass {
    private EntityManager em = ...; // local entity manager

    public void method(Entity e) { // only entity given here
        Entity localEntity = em.find(Entity.class, e.getId());
        EntityTransaction et = em.getTransaction();
        et.begin();
        ...
        // apply some modifications to the local entity's fields
        applyChanges(localEntity);
        ...
        try {
            et.commit();
            // Changes were successfully commited to the database. Also apply
            // the changes on the original entity, so they are visible in GUI.
            applyChanges(e);
        } catch (Exception ex) {
            et.rollback();
            // original entity e remains unchanged
        }
    }

    private void applyChanges(Entity e) { 
        ... 
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top