I want to recover from a failed transaction.
Now, of course, after any rollback, all objects become disconnected, and the entity manager is closed. However, the user interface still contains separate objects. Obviously, we cannot just throw away user changes, so we would like to let them try again (correct the highlighted validation error, and then click the button again).
Following the Java Persistence WikiBook ,
One method of error handling is to call a merge for each managed entity after the commit fails in the new EntityManager, and then try to commit the new EntityManager. One problem may be that any identifiers that have been assigned, or optimistic versions of locks that have been assigned or increased, may need to be reset. In addition, if the original EntityManager was EXTENDED, all objects that were in use would still be detached and should be reset.
This option seems straightforward before we inevitably encounter these expected problems. Some services can run flash for various reasons, which increases the increment of @Version fields both in the database (which is rollback) and in Java objects (which is not the case). The following save calls are merge that raise an unexpected OptimisticLockException .
Is there a reliable way to “rollback” the version fields in the Java beans entity?
Well, that’s hard. We have cascading objects with their own @Versions, so doing it manually seems fragile. (How can we reliably know the original (saved) versions in any case? It is impossible to execute the request, because another user can successfully update the entity in average time, and the request of the current version can break oplocking!)
Another more attractive error handling method is to always work with a non-transactional EntityManager. When this time is for commit, a new EntityManager is created, non-transaction objects are merged into it, and the new EntityManager is committed. If the commit fails, only the state of the new EntityManager can be inconsistent, the original EntityManager will not be affected. This can fix the problem, and the EntityManager is re-combined with 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 usual. This solution is costly and therefore should only be used if error handling is really required and the JPA does not provide alternatives.
It seems logical. Does anyone have experience implementing this kind of recovery with two EntityManager (especially with Spring)? Any errors I should know about before trying to do this? It seems that every service and DAO will now need to know about these two entity managers (with Spring, today they are almost agnostically saved). DAO "find" operations use a single EM; "update" uses a different one. Or have separate "reads" and "write" DAO. Uch.
Other options that I have considered include:
- Use the DTO in the user interface, so automatic zooming does not affect anything. Ugly.
- Move the call to
merge until the end of any linked operation. Entity joins only after all checks and status updates succeed. It seems strange that the "save" service will no longer be merge (just a check). In fact, the user interface will be responsible for calling the DAO! Is it as unusual as it seems?
Advice? Thanks: -)
My architecture update includes:
- Individual Objects Updated Using the User Interface (JSF)
- Object identifiers are NOT auto-generated (pre-assigned UUIDs and / or business keys)
- Objects have
@Version auto-increment fields for oplocking - Save service checks
em.merge (JPA over Hibernate) - Process services check, apply business logic, update status status.
- Services can be compiled. One button user interface can
- (Spring
@Transactional Tips: @Transactional ) - Save
- Process 1
- Process 2
- (
@Transactional : commit)
- Any service can raise a validation exception (JSR 303) that rolls back (messages are expected to be displayed in the user interface)