You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Hugi Thordarson <hu...@karlmenn.is> on 2015/08/07 16:16:19 UTC

Implementing and audit log using Cayenne; finding what was modified in an object

Hi all.

I’m attempting to implement an audit log using Cayenne, automatically storing information on modifications alongside every insert, update and delete.

I’m currently doing this by adding a listener on the DataDomain that watches for the lifecycle events PrePersist, PreUpdate and PreRemove, and there I log what’s happening with the objects. Works perfectly.

One thing I’m wondering though. How can I find what has changed in an object in PreUpdate (i.d. when logging modifications)? For those familiar with EOF, I do it there by looking at the EOEditingContext’s.commitedSnapshotForObject() and comparing the values found there with the object’s current values—but I can’t find how to to do something similar in Cayenne.

Am I maybe going about this in a completely wrong way (and there’s perhaps a much better, more Cayenne-ish way to do this)? If so, I’d be grateful if someone could point me in the correct direction :).

Thanks once again,
- hugi

// Hugi Thordarson
// http://www.loftfar.is/ <http://www.loftfar.is/>
// s. 895-6688

Re: Implementing and audit log using Cayenne; finding what was modified in an object

Posted by Michael Gentry <mg...@masslight.net>.
Hi Matt,

Keep in mind you are using extra memory with this approach since Cayenne
already has a snapshot of the original values.  (It needs the snapshot data
to do optimistic locking and to only send changed values in an UPDATE
statement.)

mrg


On Fri, Aug 7, 2015 at 11:19 AM, Matt Watson <ma...@swarmbox.com> wrote:

> We do this by extending the CayenneDataObject class and introducing a
> HashMap that tracks the original value of the properties that have changed.
> Override the writeProperty/setToOneTarget (we only care about auditing
> attributes and belongsTo relationships.
>
> Then when it comes time to Insert/Update then you can loop over the
> properties you want to “audit” and check if they are modified
> This map gets reset after commits.
>
> protected Map<String,Object> originalPropertyMap = new
> HashMap<String,Object>();
>
> @PostPersist
> @PostUpdate
> public void initializeOriginalPropertyMap() {
>    this.originalPropertyMap.clear();
> }
>
> @Override
> public void writeProperty(String property, Object value) {
>    if (!this.originalPropertyMap.containsKey(property)) {
>       this.originalPropertyMap.put(property, this.readProperty(property));
>    }
>
>    super.writeProperty(property, value);
> }
>
> @Override
> public void setToOneTarget(String relationship,
> org.apache.cayenne.DataObject value, boolean reverse) {
>    if (!this.originalPropertyMap.containsKey(relationship)) {
>       this.originalPropertyMap.put(relationship,
> this.readProperty(relationship));
>    }
>
>    super.setToOneTarget(relationship, value, reverse);
> }
> @Override
>    public Boolean isPropertyModified(String property) {
>       Object original = this.getOriginalProperty(property);
>       Object current = this.readProperty(property);
>
>       // if property is an attribute and array, then use array compares
>       PropertyDescriptor propertyDescriptor = Cayenne.getProperty(this,
> property);
>       if(propertyDescriptor instanceof AttributeProperty) {
>
>  if(((AttributeProperty)propertyDescriptor).getAttribute().getJavaClass().isArray())
> {
>             return !Objects.deepEquals(original, current);
>          }
>       }
>
>       return !Objects.equals(original, current);
>    }
>
>
>
> > On Aug 7, 2015, at 7:16 AM, Hugi Thordarson <hu...@karlmenn.is> wrote:
> >
> > Hi all.
> >
> > I’m attempting to implement an audit log using Cayenne, automatically
> storing information on modifications alongside every insert, update and
> delete.
> >
> > I’m currently doing this by adding a listener on the DataDomain that
> watches for the lifecycle events PrePersist, PreUpdate and PreRemove, and
> there I log what’s happening with the objects. Works perfectly.
> >
> > One thing I’m wondering though. How can I find what has changed in an
> object in PreUpdate (i.d. when logging modifications)? For those familiar
> with EOF, I do it there by looking at the
> EOEditingContext’s.commitedSnapshotForObject() and comparing the values
> found there with the object’s current values—but I can’t find how to to do
> something similar in Cayenne.
> >
> > Am I maybe going about this in a completely wrong way (and there’s
> perhaps a much better, more Cayenne-ish way to do this)? If so, I’d be
> grateful if someone could point me in the correct direction :).
> >
> > Thanks once again,
> > - hugi
> >
> > // Hugi Thordarson
> > // http://www.loftfar.is/ <http://www.loftfar.is/>
> > // s. 895-6688
>
>

Re: Implementing and audit log using Cayenne; finding what was modified in an object

Posted by Matt Watson <ma...@swarmbox.com>.
We do this by extending the CayenneDataObject class and introducing a HashMap that tracks the original value of the properties that have changed.
Override the writeProperty/setToOneTarget (we only care about auditing attributes and belongsTo relationships.

Then when it comes time to Insert/Update then you can loop over the properties you want to “audit” and check if they are modified
This map gets reset after commits.

protected Map<String,Object> originalPropertyMap = new HashMap<String,Object>();

@PostPersist
@PostUpdate
public void initializeOriginalPropertyMap() {
   this.originalPropertyMap.clear();
}

@Override
public void writeProperty(String property, Object value) {
   if (!this.originalPropertyMap.containsKey(property)) {
      this.originalPropertyMap.put(property, this.readProperty(property));
   }

   super.writeProperty(property, value);
}

@Override
public void setToOneTarget(String relationship, org.apache.cayenne.DataObject value, boolean reverse) {
   if (!this.originalPropertyMap.containsKey(relationship)) {
      this.originalPropertyMap.put(relationship, this.readProperty(relationship));
   }

   super.setToOneTarget(relationship, value, reverse);
}
@Override
   public Boolean isPropertyModified(String property) {
      Object original = this.getOriginalProperty(property);
      Object current = this.readProperty(property);

      // if property is an attribute and array, then use array compares
      PropertyDescriptor propertyDescriptor = Cayenne.getProperty(this, property);
      if(propertyDescriptor instanceof AttributeProperty) {
         if(((AttributeProperty)propertyDescriptor).getAttribute().getJavaClass().isArray()) {
            return !Objects.deepEquals(original, current);
         }
      }

      return !Objects.equals(original, current);
   }



> On Aug 7, 2015, at 7:16 AM, Hugi Thordarson <hu...@karlmenn.is> wrote:
> 
> Hi all.
> 
> I’m attempting to implement an audit log using Cayenne, automatically storing information on modifications alongside every insert, update and delete.
> 
> I’m currently doing this by adding a listener on the DataDomain that watches for the lifecycle events PrePersist, PreUpdate and PreRemove, and there I log what’s happening with the objects. Works perfectly.
> 
> One thing I’m wondering though. How can I find what has changed in an object in PreUpdate (i.d. when logging modifications)? For those familiar with EOF, I do it there by looking at the EOEditingContext’s.commitedSnapshotForObject() and comparing the values found there with the object’s current values—but I can’t find how to to do something similar in Cayenne.
> 
> Am I maybe going about this in a completely wrong way (and there’s perhaps a much better, more Cayenne-ish way to do this)? If so, I’d be grateful if someone could point me in the correct direction :).
> 
> Thanks once again,
> - hugi
> 
> // Hugi Thordarson
> // http://www.loftfar.is/ <http://www.loftfar.is/>
> // s. 895-6688


Re: Implementing and audit log using Cayenne; finding what was modified in an object

Posted by Hugi Thordarson <hu...@karlmenn.is>.
Thanks a lot for your help Michael, I’m going to try my hand at this today and post my results to the list.

Cheers,
- hugi

// Hugi Thordarson
// http://www.loftfar.is/ <http://www.loftfar.is/>
// s. 895-6688



> On 7. ágú. 2015, at 16:51, Michael Gentry <mg...@masslight.net> wrote:
> 
> Hi Hugi,
> 
> I was mainly interest in attributes, so that's all I coded up.  I'm sure
> there is a way to take relationships into account, as well, but I'd have to
> code it up and test it and don't have time at the moment (maybe over the
> weekend).  If someone else, including yourself, beats me to it, that would
> be great.
> 
> I'd start by looking at getRelationships():
> 
> http://cayenne.apache.org/docs/3.0/api/org/apache/cayenne/map/ObjEntity.html
> 
> mrg
> 
> 
> On Fri, Aug 7, 2015 at 12:20 PM, Hugi Thordarson <hu...@karlmenn.is> wrote:
> 
>> Thanks a lot Michael, objectStore.getSnapshot( object.getObjectId() );
>> does almost exactly what I needed.
>> 
>> One thing though, this code does not handle modifications to
>> relationships, since my foreign keys are not modeled as attributes in the
>> ObjEntity. Do you think there’s some way to get at changes to modeled
>> foreign keys as well?
>> 
>> Cheers,
>> - hugi
>> 
>> 
>> 
>> 
>>> On 7. ágú. 2015, at 14:33, Michael Gentry <mg...@masslight.net> wrote:
>>> 
>>> Hi Hugi,
>>> 
>>> Maybe something like isIdenticalToSnapshot() in this Gist will help you?
>>> 
>>> https://gist.github.com/mrg/4dce22b67175c27f4047
>>> 
>>> If I recall, this version is for Cayenne 3.0, so there might be a few
>>> tweaks needed for 3.1.
>>> 
>>> mrg
>>> 
>>> 
>>> On Fri, Aug 7, 2015 at 10:16 AM, Hugi Thordarson <hu...@karlmenn.is>
>> wrote:
>>> 
>>>> Hi all.
>>>> 
>>>> I’m attempting to implement an audit log using Cayenne, automatically
>>>> storing information on modifications alongside every insert, update and
>>>> delete.
>>>> 
>>>> I’m currently doing this by adding a listener on the DataDomain that
>>>> watches for the lifecycle events PrePersist, PreUpdate and PreRemove,
>> and
>>>> there I log what’s happening with the objects. Works perfectly.
>>>> 
>>>> One thing I’m wondering though. How can I find what has changed in an
>>>> object in PreUpdate (i.d. when logging modifications)? For those
>> familiar
>>>> with EOF, I do it there by looking at the
>>>> EOEditingContext’s.commitedSnapshotForObject() and comparing the values
>>>> found there with the object’s current values—but I can’t find how to to
>> do
>>>> something similar in Cayenne.
>>>> 
>>>> Am I maybe going about this in a completely wrong way (and there’s
>> perhaps
>>>> a much better, more Cayenne-ish way to do this)? If so, I’d be grateful
>> if
>>>> someone could point me in the correct direction :).
>>>> 
>>>> Thanks once again,
>>>> - hugi
>>>> 
>>>> // Hugi Thordarson
>>>> // http://www.loftfar.is/ <http://www.loftfar.is/>
>>>> // s. 895-6688
>> 
>> 


Re: Implementing and audit log using Cayenne; finding what was modified in an object

Posted by Michael Gentry <mg...@masslight.net>.
Hi Hugi,

I was mainly interest in attributes, so that's all I coded up.  I'm sure
there is a way to take relationships into account, as well, but I'd have to
code it up and test it and don't have time at the moment (maybe over the
weekend).  If someone else, including yourself, beats me to it, that would
be great.

I'd start by looking at getRelationships():

http://cayenne.apache.org/docs/3.0/api/org/apache/cayenne/map/ObjEntity.html

mrg


On Fri, Aug 7, 2015 at 12:20 PM, Hugi Thordarson <hu...@karlmenn.is> wrote:

> Thanks a lot Michael, objectStore.getSnapshot( object.getObjectId() );
> does almost exactly what I needed.
>
> One thing though, this code does not handle modifications to
> relationships, since my foreign keys are not modeled as attributes in the
> ObjEntity. Do you think there’s some way to get at changes to modeled
> foreign keys as well?
>
> Cheers,
> - hugi
>
>
>
>
> > On 7. ágú. 2015, at 14:33, Michael Gentry <mg...@masslight.net> wrote:
> >
> > Hi Hugi,
> >
> > Maybe something like isIdenticalToSnapshot() in this Gist will help you?
> >
> > https://gist.github.com/mrg/4dce22b67175c27f4047
> >
> > If I recall, this version is for Cayenne 3.0, so there might be a few
> > tweaks needed for 3.1.
> >
> > mrg
> >
> >
> > On Fri, Aug 7, 2015 at 10:16 AM, Hugi Thordarson <hu...@karlmenn.is>
> wrote:
> >
> >> Hi all.
> >>
> >> I’m attempting to implement an audit log using Cayenne, automatically
> >> storing information on modifications alongside every insert, update and
> >> delete.
> >>
> >> I’m currently doing this by adding a listener on the DataDomain that
> >> watches for the lifecycle events PrePersist, PreUpdate and PreRemove,
> and
> >> there I log what’s happening with the objects. Works perfectly.
> >>
> >> One thing I’m wondering though. How can I find what has changed in an
> >> object in PreUpdate (i.d. when logging modifications)? For those
> familiar
> >> with EOF, I do it there by looking at the
> >> EOEditingContext’s.commitedSnapshotForObject() and comparing the values
> >> found there with the object’s current values—but I can’t find how to to
> do
> >> something similar in Cayenne.
> >>
> >> Am I maybe going about this in a completely wrong way (and there’s
> perhaps
> >> a much better, more Cayenne-ish way to do this)? If so, I’d be grateful
> if
> >> someone could point me in the correct direction :).
> >>
> >> Thanks once again,
> >> - hugi
> >>
> >> // Hugi Thordarson
> >> // http://www.loftfar.is/ <http://www.loftfar.is/>
> >> // s. 895-6688
>
>

Re: Implementing and audit log using Cayenne; finding what was modified in an object

Posted by Hugi Thordarson <hu...@karlmenn.is>.
Thanks a lot Michael, objectStore.getSnapshot( object.getObjectId() ); does almost exactly what I needed.

One thing though, this code does not handle modifications to relationships, since my foreign keys are not modeled as attributes in the ObjEntity. Do you think there’s some way to get at changes to modeled foreign keys as well?

Cheers,
- hugi




> On 7. ágú. 2015, at 14:33, Michael Gentry <mg...@masslight.net> wrote:
> 
> Hi Hugi,
> 
> Maybe something like isIdenticalToSnapshot() in this Gist will help you?
> 
> https://gist.github.com/mrg/4dce22b67175c27f4047
> 
> If I recall, this version is for Cayenne 3.0, so there might be a few
> tweaks needed for 3.1.
> 
> mrg
> 
> 
> On Fri, Aug 7, 2015 at 10:16 AM, Hugi Thordarson <hu...@karlmenn.is> wrote:
> 
>> Hi all.
>> 
>> I’m attempting to implement an audit log using Cayenne, automatically
>> storing information on modifications alongside every insert, update and
>> delete.
>> 
>> I’m currently doing this by adding a listener on the DataDomain that
>> watches for the lifecycle events PrePersist, PreUpdate and PreRemove, and
>> there I log what’s happening with the objects. Works perfectly.
>> 
>> One thing I’m wondering though. How can I find what has changed in an
>> object in PreUpdate (i.d. when logging modifications)? For those familiar
>> with EOF, I do it there by looking at the
>> EOEditingContext’s.commitedSnapshotForObject() and comparing the values
>> found there with the object’s current values—but I can’t find how to to do
>> something similar in Cayenne.
>> 
>> Am I maybe going about this in a completely wrong way (and there’s perhaps
>> a much better, more Cayenne-ish way to do this)? If so, I’d be grateful if
>> someone could point me in the correct direction :).
>> 
>> Thanks once again,
>> - hugi
>> 
>> // Hugi Thordarson
>> // http://www.loftfar.is/ <http://www.loftfar.is/>
>> // s. 895-6688


Re: Implementing and audit log using Cayenne; finding what was modified in an object

Posted by Michael Gentry <mg...@masslight.net>.
Oh, and printDirty() actually dumps everything that is modified in the
DataContext, which is kind of an uncommitted audit log.


On Fri, Aug 7, 2015 at 10:33 AM, Michael Gentry <mg...@masslight.net>
wrote:

> Hi Hugi,
>
> Maybe something like isIdenticalToSnapshot() in this Gist will help you?
>
> https://gist.github.com/mrg/4dce22b67175c27f4047
>
> If I recall, this version is for Cayenne 3.0, so there might be a few
> tweaks needed for 3.1.
>
> mrg
>
>
> On Fri, Aug 7, 2015 at 10:16 AM, Hugi Thordarson <hu...@karlmenn.is> wrote:
>
>> Hi all.
>>
>> I’m attempting to implement an audit log using Cayenne, automatically
>> storing information on modifications alongside every insert, update and
>> delete.
>>
>> I’m currently doing this by adding a listener on the DataDomain that
>> watches for the lifecycle events PrePersist, PreUpdate and PreRemove, and
>> there I log what’s happening with the objects. Works perfectly.
>>
>> One thing I’m wondering though. How can I find what has changed in an
>> object in PreUpdate (i.d. when logging modifications)? For those familiar
>> with EOF, I do it there by looking at the
>> EOEditingContext’s.commitedSnapshotForObject() and comparing the values
>> found there with the object’s current values—but I can’t find how to to do
>> something similar in Cayenne.
>>
>> Am I maybe going about this in a completely wrong way (and there’s
>> perhaps a much better, more Cayenne-ish way to do this)? If so, I’d be
>> grateful if someone could point me in the correct direction :).
>>
>> Thanks once again,
>> - hugi
>>
>> // Hugi Thordarson
>> // http://www.loftfar.is/ <http://www.loftfar.is/>
>> // s. 895-6688
>
>
>

Re: Implementing and audit log using Cayenne; finding what was modified in an object

Posted by Michael Gentry <mg...@masslight.net>.
Hi Hugi,

Maybe something like isIdenticalToSnapshot() in this Gist will help you?

https://gist.github.com/mrg/4dce22b67175c27f4047

If I recall, this version is for Cayenne 3.0, so there might be a few
tweaks needed for 3.1.

mrg


On Fri, Aug 7, 2015 at 10:16 AM, Hugi Thordarson <hu...@karlmenn.is> wrote:

> Hi all.
>
> I’m attempting to implement an audit log using Cayenne, automatically
> storing information on modifications alongside every insert, update and
> delete.
>
> I’m currently doing this by adding a listener on the DataDomain that
> watches for the lifecycle events PrePersist, PreUpdate and PreRemove, and
> there I log what’s happening with the objects. Works perfectly.
>
> One thing I’m wondering though. How can I find what has changed in an
> object in PreUpdate (i.d. when logging modifications)? For those familiar
> with EOF, I do it there by looking at the
> EOEditingContext’s.commitedSnapshotForObject() and comparing the values
> found there with the object’s current values—but I can’t find how to to do
> something similar in Cayenne.
>
> Am I maybe going about this in a completely wrong way (and there’s perhaps
> a much better, more Cayenne-ish way to do this)? If so, I’d be grateful if
> someone could point me in the correct direction :).
>
> Thanks once again,
> - hugi
>
> // Hugi Thordarson
> // http://www.loftfar.is/ <http://www.loftfar.is/>
> // s. 895-6688