Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA transaction rollback retry and recovery: merging entity with auto-incremented @Version

I'd like to recover after a failed transaction.

Now, of course after any rollback, all entities become detached and the entity manager is closed. However, the UI still holds the detached entities. Obviously we can't just throw away the user's changes, so we'd like to let them retry (fix the highlighted validation error, then click the button again).

Following the Java Persistence WikiBook,

One method of error handling is to call merge for each managed object after the commit fails into a new EntityManager, then try to commit the new EntityManager. One issue may be that any ids that were assigned, or optimistic lock versions that were assigned or incremented may need to be reset. Also, if the original EntityManager was EXTENDED, any objects that were in use would still become detached, and need to be reset.

This option seems straightforward at first, until we inevitably encounter exactly those anticipated issues. Some services might trigger a flush for various reasons, which increments @Version fields in both the DB (which is rolled back) and Java entities (which are not). The next "save" calls merge, which throws an unexpected OptimisticLockException.

Is there a reliable way to "rollback" version fields in the Java entity beans?

OK, that seams hard. We have cascaded entities with their own @Versions, so doing it manually seems fragile. (How can we reliably know the original (persisted) versions anyway? Can't query, because some other user might successfully update the entity in the mean time; querying the current version could break oplocking!)

Another more involved method to error handling is to always work with a non-transactional EntityManager. When it's time to commit, a new EntityManager is created, the non-transactional objects are merged into it, and the new EntityManager is committed. If the commit fails, only the state of the new EntityManager may be inconsistent, the original EntityManager will be unaffected. This can allow the problem to be corrected, and the EntityManager re-merged into another new EntityManager. If the commit is successful any commit changes can be merged back into the original EntityManager, which can then continue to be used as normal. This solution requires a fair bit of overhead, so should only be used if error handling is really required, and the JPA provider provides no alternatives.

This seems logical. Does anyone have any experience implementing this kind of recovery with two EntityManagers (especially with Spring)? Any pitfalls I should be aware of before attempting it? It seems like every service and DAO would now have to become aware of the two entity managers (with Spring, today they are almost persistence-layer agnostic). DAO 'find' operations use one EM; 'update' uses another. Or have separate 'read' and 'write' DAOs. Ouch.

Other options I've considered include:

  • Use DTOs in the UI, so auto-incrementing does not affect anything. Ugly.
  • Move call to merge to the end of any composed operation. Entity is only attached after all validation and state updates have succeeded. Seems strange that the "save" service would no longer merge (only validate). In effect, the UI would take responsibility for calling the DAO! Is this as unusual as it sounds?

Advice? Thanks :-)

Update My architecture includes:

  • Detached entities updated by UI (JSF)
  • Entity IDs are NOT autogenerated (pre-assigned UUIDs and/or business keys)
  • Entities have auto-incremented @Version fields for oplocking
  • "Save" service validates, calls em.merge (JPA over Hibernate)
  • "Process" services validate, apply business logic, update entity state
  • Services can be composed. One UI button might do
    1. (Spring @Transactional advice around UI controller: begin)
    2. Save
    3. Process 1
    4. Process 2
    5. (@Transactional: commit)
  • Any service might throw a validation exception (JSR 303), which rolls back as expected (messages are displayed in the UI)
like image 504
Peter Davis Avatar asked Jun 26 '11 06:06

Peter Davis


2 Answers

You ask two different questions and then a general call for advice. I'm going with the advice (would have put this in a comment but SO won't let me comment yet...)

Using DTO's (or any UI-only data structure) in the UI is not ugly - it's architecturally sound. I agree with JB Nizet that the non-transactional EM approach essentially turns your entities into DTO's. If you must use entities in your UI (and I realize Spring encourages this), this is the way to go.

In my experience the main reason people want to use entities as UI scratch storage is to get access to the complex validation logic therein. You might also want to consider refactoring towards making that logic accessible without invoking your full domain model.

like image 171
jmetcher Avatar answered Nov 17 '22 02:11

jmetcher


Can only support JB Nizet's comment: merge only copies the state of the detached entities.

like image 35
Marcel Stör Avatar answered Nov 17 '22 02:11

Marcel Stör