0

I am writing a wizard to setup a fairly complex object model. The desired behavior is to load values into the wizard out of the database using JPA, edit them through various text fields and combo boxes, and write them back only if the user clicks "Finish" at the end. If the user clicks "Cancel," I want the contents of the database to remain as they were.

The approach I have in mind is:

EntityManager em1; // From the Factory
EntityTransaction tx1 = em1.getTransaction();
tx1.begin();
List<?> docs = em1.createQuery("select d from DocumentRoot d").getResultList();
DocumentRoot doc = (DocumentRoot)docs.get(0);
tx1.commit();
em1.close(); // Now DocumentRoot and all associated objects are detached

if (DocumentEditor.showEditDialog(doc) == APPROVE) { // Mutates doc up the wazoo
    EntityManager em2; // From the Factory
    EntityTransaction tx2 = em2.getTransaction();
    tx2.begin();
    em2.merge(doc);
    tx2.commit();
    em2.close();
}

There is probably some balance of annotations that make this work, but I haven't stumbled upon it. I added cascade = CascadeType.ALL on all my one-to-many etc. associations, which fixed the "object references an unsaved transient instance" errors, but now I have "Multiple representations of the same entity [...] are being merged."

I have some inkling that the object in question is multiply-represented because there's a many-to-many representation. There's another thread out there suggesting removing CascadeType.MERGE from references to that object, but this seems like a dangerously leaky bandaid. Unsurprisingly, removing that CascadeType.MERGE meant that new entities of the type were not persisted on merge.

It seems like this whole approach -- detach, edit, merge if accepted -- is making JPA very unhappy. Is there another approach that is considered more idiomatic for cases where you want to edit what is effectively a local copy without persisting, and only persisting when the user clicks "OK?"

Community
  • 1
  • 1
pgoldste
  • 105
  • 7

1 Answers1

0

You are right, I also experienced that editing JPA entities is cumbersome when using detach -> edit -> merge mode.

If you use Java EE, better approach is to use entityManager in extended mode, as Adam Bien explains in his blog. He describes how to create gateways instead of facades/DAOs, but what you essentially need is a stateful EJB which holds holds entityManager with extendend context (@PersistenceContext(type=PersistenceContextType.EXTENDED)). The EJB must be held in a session scoped bean (any other scope which contains both data retrieval and saving is OK)).

More info about extended EM is here.

If you don't use Java EE, then take care of the places, where you copy entities you don't copy by reference, but always create a new entity if they really represent a different instance (more info about the problem: https://hibernate.atlassian.net/browse/HHH-9106).

Or follow a more robust, but more cumbersome pattern that you always treat incoming entities as detached and instead merge, you would retrieve every entity from DB by ID and copy all changed data. But I repeat, this is very cumbersome and error-prone when you operate on a big tree of entities.

OndroMih
  • 7,280
  • 1
  • 26
  • 44