Envers: create multiple revisions in one transaction

Hello everybody,

I have a running application using envers and I am facing the following problem:
My entity should track a functional status value to retrieve the history of modifications. I apply these steps to the entity:

  1. set current version to “outdated”
  2. update the entity from by calling an external resource

This should result in 3 audit records:
Rev #1: consistent
Rev #2: outdated
Rev #3 consistent

When I summarize both steps into one transaction Rev #2 gets lost.

I’ve tracked down that envers creates only one revision at transaction.commit() - thats what I understood is a architectural decision.

Is there a possibility to force creation of an revision on a flush() to track consistently all modifications in one transaction?

When I split the steps into 2 transactions everything works smooth. My goal is to put both operations into 1 transaction. By doing this Rev#2 gets lost.
When I split both operations into seperate transactions everything works fine. Do you have an idea how to achieve this behaviour with envers within 1 single transaction?

Frank

Hibernate Envers, as you figured, only creates the revisions before transaction commit. You could try to invoke the pre commit listener manually by using AuditProcessManager.get(session).doBeforeTransactionCompletion(session) but I haven’t tried that myself yet.

Thank you for your tip. But I couldn’t find out how to access the AuditProcessManager. Can you explain me how to access it?

You need something like this sessionFactory.getServiceRegistry().getService(EnversService.class).getAuditProcessManager()

Hello,
I am facing the same problem - I need to make more updates of audited entity in one transaction and need to have record in audit table for each update. Is there meanwhile more information to the topic?
I’m not sure if I got the hint with AuditProcessManager.get(session).doBeforeTransactionCompletion(session) right? Should I try to trigger it from the code at the time when I want to create new audit record? (In my case after each update?)

Yeah, you understood that correctly.

I tried this:

private void forceCreateEnversAudition() {
        Session hibernateSession = this.entityManager.unwrap(Session.class);
        SessionFactory sessionFactory = hibernateSession.getSessionFactory();
        AuditProcessManager apm = sessionFactory.getSessionFactory().getServiceRegistry().getService(EnversService.class).getAuditProcessManager();
        apm.get(**session**).doBeforeTransactionCompletion(**session**);
    }

The input parameter session of the method AuditProsessManager.get(EventSource session) is of type EventSource (I thought it was Session at first). I have no idea what should I provide there.

The session implements that interface, you can cast the session.

Ah, I see, thanks :slight_smile:

I called this:

 private void forceCreateEnversAudition() {
        Session hibernateSession = this.entityManager.unwrap(Session.class);
        SessionFactory sessionFactory = hibernateSession.getSessionFactory();
        AuditProcessManager apm = ((SessionFactoryImplementor) sessionFactory).getServiceRegistry().getService(EnversService.class).getAuditProcessManager();
        apm.get((EventSource) hibernateSession).doBeforeTransactionCompletion((SessionImplementor) hibernateSession);
    }

after this.entityManager.persist() and after corresponding entity properties change in case of update, but see no change - only 1 record in audition table.

Did you call hibernateSession.flush() before?

Yes, I tried both with and without flush.

this.entityManager.flush()

AFAICT this should cause the creation of AuditWorkUnit. If that doesn’t work, then this simply can’t be done right now. Chances are, it is possible but there is some other problem with how you try to use it. Maybe you can debug this a bit to understand where this might go wrong.

After little debugging I found out following:

  • the saving of audit entity was triggered AuditStrategy.perform() was called

  • in database table I could see the audit record with correct values

  • at the end of the transaction, the same audit record contained new values - it was updated => only 1 audit record per audited entity and transaction in audit table

Hmm, that is unfortunate. I don’t know where this happens or if this can be worked around. Maybe you can get around this if you invoke the afterTransactionCompletion on SessionImplementor, but I don’t know what effects this will have on the rest of the transaction.

I 've added this call at the end of the method:

private void forceCreateEnversAudition() {
        this.entityManager.flush();
        Session hibernateSession = this.entityManager.unwrap(Session.class);
        SessionFactory sessionFactory = hibernateSession.getSessionFactory();
        AuditProcessManager apm = ((SessionFactoryImplementor) sessionFactory).getServiceRegistry().getService(EnversService.class).getAuditProcessManager();
        apm.get((EventSource) hibernateSession).doBeforeTransactionCompletion((SessionImplementor) hibernateSession);
        **((SessionImplementor) hibernateSession).afterTransactionCompletion(true, false);**
    }

At first glance it looks fine - i can see also the previous versions in audit table, each has new REV number.