3 Replies Latest reply on Mar 22, 2017 11:21 PM by ccranfor

    Manually trigger a revision in Envers.

    chrisknoll

      I'm thinking of leveraging Envers in my application for the following reasons:

       

      1. The application uses JPA 2.1 provided by Hibernate, and the annotations for entities and relationships is understood by Envers via the @Audit annotations.

      2. I like the APIs to query for revisions.

      3. The ddl can be generated for the audit tables using the existing entity definitions.

       

      In my application, you can consider the data in the main Entity tables to be 'draft' revisions that users can iterate through updates, and then when the user decides that this is 'one for the history books', they can trigger an action which will cause an envers entity revision to materialize.

       

      But, this means that an entity might exist without any data in the _aud table. My understanding is that this is a problem...Also, I can't find any examples of triggering the audit event manually.  I found examples of 'conditional auditing' but that's not what I want here for the following reasons:

      1. I don't want to have to alter my entities to add some sort of data flag where you look at the flag to determine if the update should result in a revision, and then during that process reset the flag to the un-revisioned state.

      2. I don't want to have any 'seed data' in the _aud table, and it seems that you have to have the initial state of the revision in the _aud table.

       

      So, my question is: can enverse do what I want, and if so, how do I signal an entity to be recorded into the _aud table without going through the entity lifecycle events (post update, etc...)

       

      Thank you for your attention!

       

      -Chris

        • 1. Re: Manually trigger a revision in Envers.
          ccranfor

          There is presently no specific way to trigger a revision of an entity, this just happens automatically as a part of the entity's lifecycle.

           

          Presently, the suggested solution would be to use conditional auditing for this; however in designing a better conditional auditing approach for Hibernate Envers 6 using annotations rather than custom listener implementations like the documentation suggests today, I uncovered that conditional auditing can be problematic for ValidityAuditStrategy users when you elect to not audit the initial INSERT operation for an entity and later allow an audit for an UPDATE operation.  So long as the initial INSERT is allowed, there is no issue, so beware if you wanted to use this route and you use the ValidityAuditStrategy.  If you're using the DefaultAuditStrategy, there is no problem with conditional auditing.

           

          Another option you didn't mention could involve the use of two entity types.  Imagine for the moment you have two entities which derive from the same mapped superclass like as follows:

          @MappedSuperclass
          public abstract class AbstractEntity {  
            @Id
            private Integer id;
            private String data;
          }
          
          @Entity
          public class DraftEntity extends AbstractEntity {
          
          }
          
          @Entity
          @Audited
          @AuditOverride(forClass=AbstractEntity.class,isAudited=true)
          public class SavedEntity extends AbstractEntity {
          
          }
          

           

          The idea is that SavedEntity acts as a shadow-copy of your drafts.  That way users can make any number of repeatable, non-audited changes to the draft entity and when your business process dictates a snapshot is to be taken, you merely copy the attributes from DraftEntity into the respective SavedEntity.  From there, Envers keeps a running audit history of all changes made to a given SavedEntity.

          • 2. Re: Manually trigger a revision in Envers.
            chrisknoll

            So I do like this idea, one of the complications is that it's a complex graph of entity relationships which are involved in this 'asset', but the nice thing about subclassing is that it inherits all the attributes automaticalyl, but one thing that is a bit more complicated is the -To-Many entities will have to have the collection types overridden in the subclass, and I think that's where it gets a bit complicated. 

             

            I am aware of the AttributeOverrides you can apply to the subclasses, so I could override things like the table they save to but I'm not entirely sure how the syntax works so I'd have to dig in on that....and I wonder if just subclassing the 'root' entity is enough, and set the AttributeOverride on the parent entity which will flow through to the -ToMany relationships that are attached to the root entity object such that the tables that they are persisted to the tables for the 'savedEntity' asset.

             

            So in your example, let's say you have a DraftEntity which has one or more 'ChildDraftElements' (or maybe these can be called 'DraftElements')...do you know if AttributeOverride can change the join table and main table of the DraftElement to persist to a different table?

            • 3. Re: Manually trigger a revision in Envers.
              ccranfor

              Yes you can do that with the @AssociationOverride annotation.  From the java documentation:

              May to be applied to an entity that extends a mapped superclass to override a relationship mapping defined by the mapped superclass.  If not specified, the association is mapped the same as in the original mapping.  When used to override a mapping defined by a mapped superclass, AssociationOverride is applied to the entity class.

              Furthermore the documentation points out if the original mapping uses a @JoinTable, the override must also explicitly include the joinTable element of the @AssociationOverride too.

               

              See AssociationOverride (Java(TM) EE 7 Specification APIs)