I'm using Hibernate Envers to persist object history. At certain points we want to capture a snapshot of the object graph state - and we can do this by knowing the corresponding Envers revision which we then store on an audit record.
However we have a problem. The parent object is updated within the same transaction where we create and store its child audit record - complete with Envers revision. We can get the latest revision:
Number revision = reader.getRevisionNumberForDate(new Date(Long.MAX_VALUE));
or create a new revision:
Number revision = reader.getCurrentRevision(DefaultRevisionEntity.class, true).getId();
and use either, but the commit of the parent always happens after that. And that is when Envers increments the revision. Hence the revision we actually need to reference in the audit record is always higher than the value stored. In the simplest case, we get and store revision N but the parent version we need is stored as N + 1.
The AuditReader reader reference can be obtained with:
JpaTransactionManager transactionManager; // injected
EntityManagerFactory emf = transactionManager.getEntityManagerFactory();
EntityManager entityManager = emf.createEntityManager();
AuditReader reader = AuditReaderFactory.get(entityManager);
We're using Spring 3 @Transactional annotations and Hibernate 4.2.
Minimal class graph:
Parent.class
int version // for hibernate optimistic locking
String revisionName
List<AuditChild> audits
AuditChild.class
int enversRevision // use to retrieve previous graphs of parent
I've tried numerous approaches to force the commit of parent to occur first, among them:
- Splitting the code across multiple methods with @Transactional(propagation = Propagation.REQUIRES_NEW)
- Explicit invocation of entityManager.flush();
Everything I've tried has either had no effect or caused other problems. I'd be happy to hear of solutions which have worked for others. Thanks.