Implementing workflow with savepoints

I’m currently migrating old client-server application to hibernate, and would like to get some advice as to best way to implement following workflow.

Suppose we have a Contract entity, which refers to several Participant entity, among others. In current 2-tier application client starts a transaction with some row in contract table, modifies it during multiple requests and then either decides to commit a transaction or cancel (rollback).
During this long-running multi-request transaction client may several times open a sub-form, to edit particular Participant. This sub-form must follow the same logic – client make some changes to participant and can either confirm those or discard. In 2-tier application this logic is implemented via savepoints – before opening participant sub-form client creates a savepoint and if user chooses to discard current changes when closing a sub-form, server performs rollback to savepoint.

Now, in real application both Contract and Participant are highly complex entities, Contract row may directly or indirectly refer to hundreds of other tables, and Participant row – to dozens of other tables.

So, currently I’m attempting to implement this with hibernate in the following way

I have two instances of extended EntityManager, one for contract form, one for participant sub-form. Contract EM loads contract entity and persists it, when user asks an application to save all changes in main contract form
When user opens a Participant sub-form, selected or new participant entity is merged into Participant EM, and sub-form deals with this merged-managed-by-participant-EM participant entity
If user chooses to confirm his changes, then participant entity is merged back into contract EM.

The problem I’ve encountered with this setup is that after upgrading hibernate from 6.2.7.Final to 6.6.7.Final this workflow started to throw StaleObjectStateException (due to fix for HHH-1661)

What is proper way to implement such workflow, when changing temporary state may need to be reverted not to its original state, but to some “savepoint”?

Thanks in advance for any advice, suggestions, pointers

Hello @egor.duda, first of all, you should never use multiple EntityManagers (i.e. Hibernate Sessions) to work on the same entities at the same time.

You should always use a single instance of EntityManager to handle your persistence needs, and if you do, you should be able to handle merging the new entity instance and adding it to a persistent one correctly. I don’t think there’s anything else wrong with your approach, obviously there could be alternatives (using temporary entities/tables for “non-definitive” data, keeping persistent state in memory) but without knowing the details of your application or how your mappings looks it’s hard to give a specific suggestion.

Thanks for prompt answer!

I understand that same entity should not be managed by two entity managers simultaneously.
Participant EM never writes anything to the database (all its operations are made within methods with TransactionAttributeType.NOT_SUPPORTED, and after user confirms or discards changes in participant sub-form, participant EM is cleared. All dml’s are then performed by Contract EM, when user submits is main contract form.

Essentially, Participant EM is used in “read-only” mode to lazy-load some children/grandchildren/etc. for participant. As I’ve said earlier, both Contract and Participant entities are highly complex, with lot of children. Loading all huge tree of data relevant to Contract, when user opens main form, is both time-consuming and unneeded (as user may never venture into parts of form using some parts of the Contract entity “tree of children”). So this parts are going to be loaded lazily. By using extended EntityManager I’m able to prevent entities from being detached from EM, and thus prevent lazy loading errors.

If I’d try to use single EntityManager then I’d have a problem of trying to have two managed instances of the same Participant entity with the same Id in the same EM, which is, I suppose, is impossible. I need two instances exactly because one is last confirmed state of data for participant and second is the state currently being edited, which may be confirmed or discarded later.

I’ll try to make a simple demo app to show supposed workflow, to illustrate the problem I’m encountering.

I’m sorry but, as I said, mixing managed entities from multiple Hibernate Sessions is unsupported and will lead to errors. You simply cannot mix managed instances from two difference persistence contexts.

If your only reason from doing this is handling lazy-loading, you should really consider using fetch graphs / dynamic fetching in queries instead. Please read this chapter of the user guide to understand how to dynamically choose which branches of your entity graphs you should fetch, and try to implement a strategy that allows you to do this in a single Hibernate session.