7 Replies Latest reply on Feb 2, 2012 6:23 PM by asheffey

    Envers and Association Querying

    asheffey

      I've been trying Envers, and so far its been virtually painless to get auditing rolling (in both parent and child entities), as well as to query the parent audited entity.

       

      However, when that parent entity is queried using getAuditReader().createQuery().forRevisionsOfEntity(MyClass.class, false, true), I'm having trouble with associations.

       

      What I am trying to do is to return the audited parent with that revision's association(s) fully populated.  For instance, in a @OneToMany, the child association should have the full List of associated entities for that revision, not just the row(s) that were inserted as a result of auditable changes.

       

      Some more info, from Eclipse debug:

      • OneToMany list associations are shown as SetProxy<U>, and when I click on them, Eclipse displays com.sun.jdi.InvocationException occurred invoking method
      • ManyToOne associations are shown as MyClass_$$_javassist_14, all properties are null, and when I click on it, the same InvocationException is thrown

       

      Thanks in advance,

       

      Aaron

        • 1. Re: Envers and Association Querying
          asheffey

          Some more data.  If in my DAO I call the getter method for an association that does not have an audit record (e.g., the association didn't have changed data), the exception "org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.foo.ChildClass#83]".

           

          My assumption would be that it would call the active version of the association, or the most recent revision.

          • 2. Re: Envers and Association Querying
            vyacheslav86

            audit proxy quieries for most recent version in the sense that

             

            maxERevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");

             

            https://github.com/hibernate/hibernate-orm/blob/master/hibernate-envers/src/main/java/org/hibernate/envers/strategy/DefaultAuditStrategy.java

             

            why would default implementation return current data? or most recent revision from the future?

             

            You can implement you proxy doing that, but i suggest to return just null instead of wrong historic data.

             

            Here we discuss initial revision generation https://community.jboss.org/thread/177321?tstart=0

             

            It is not yet implemented and kind of hard.

            • 3. Re: Envers and Association Querying
              asheffey

              What I'm trying to return is the state of an entity, and its associated children and parents, at a given point in time.  My situation:

               

              • Entity 1 has a OneToMany relationship with Entity 2, where Entity 2 is a list of Entity 1's properties
              • Entity 1 has a ManyToOne relationship with Entity 3, where many Entity 1s are associated with one Entity 3
              • When I make a change to Entity 1 and/or an item in Entity 1's Entity 2 list, I correctly get audit records persisted for Entity 1 and the Entity 2 list item that I updated
              • At this point, I have an Entity 1 audit record; one Entity 2 audit record, but not the full Entity 2 list; and I don't have an Entity 3 audit record
              • When I query as per my original post, I get the Entity 1 audit record, but am unable to access any assoicated entities from that audit record using their get methods

               

              In this situation, what is the best practice for returning the entire state of Entity 1 at a given revision?

              • 4. Re: Envers and Association Querying
                vyacheslav86

                Post code please. I don't understand: Entity 1 has a OneToMany relationship with Entity 2, where Entity 2 is a list of Entity 1's properties

                 


                • 5. Re: Envers and Association Querying
                  asheffey

                  Apologies, I was using a soft term, not a technical term.  Code:

                   

                  @Audited

                  @Entity

                  @Table(name="tb_entity1")

                  public class Entity1 implements Serializable {

                       @OneToMany(fetch = FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="entity1")

                       @BatchSize(size=16)

                       Set<Entity2> entity1Properties;

                   

                       @ManyToOne(fetch = FetchType.LAZY)

                       @JoinColumn(name="entity3_id")

                       Entity3 entity3;

                  }

                  • 6. Re: Envers and Association Querying
                    vyacheslav86

                    Here is the thing: are you deploying audit to already filled db or empty db?

                     

                    >> one Entity 2 audit record, but not the full Entity 2 list

                     

                    Does that mean that you have entity2 list in db before audit was deployed?

                     

                    If so - that's what i'm talking about: envers audit only work for correct db which is db audited from empty state.

                    If you need to deploy audit to existing db you need to create initial revision data by hand.

                     

                    https://community.jboss.org/thread/177321?tstart=0

                    • 7. Re: Envers and Association Querying
                      asheffey

                      Yes, I've added Envers into an existing DB with extant data.  I'll try adding in the missing data for a test case to see if that corrects the issue.

                       

                      EDIT:

                       

                      After a good deal of work, I was able to get everything working correctly.  Details:

                      • All audited elements had to be level set with baseline audit records.  I've attached a sample file, a Postgres function, to show how I did it.  I'm not sure this step could be abstracted, as it appears you need to create lookup table records first so that your downstream tables' audit records are later in time than the lookup tables' records, and only you know that for certain.
                      • All audited elemens needed to be initialized in my DAO; once the baseline audit records were there, using Hibernate.initialize did the trick.

                       

                      Thanks to Vyacheslav's clue, things are working great!  Now its on to getting custom RevisionEntity rolling.


                      Aaron