You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Andrew Willerding <aw...@itsurcom.com> on 2019/01/22 16:06:44 UTC

How to get the changes to an object in PreUpdate?

I can't seem to find documentation, or better yet some examples for 4.0 
or 4.1 that explains how to check for either a single property change or 
how to get a list of the property changes in an onPreUpdate callback. 
Unless I'm missing something obvious (entirely likely ;-) ) the 
Lifecycle events documentation doesn't seem to mention any way to do 
this.  Is this something that I need to manually track in the application?

Also, what should I do if I want to reject the changes inside of an 
onPreUpdate callback?  Do I need to do an explicit rollback or simply 
throw a CayenneRuntimeException of some sort?  I'm guessing it's an 
explicit rollback.

Thanks,

Andrew




Re: How to get the changes to an object in PreUpdate?

Posted by Andrew Willerding <aw...@itsurcom.com>.
After some more Googling, I found this ...

https://stackoverflow.com/questions/9899873/getting-a-fresh-data-object-in-apache-cayenne

...and modified my onPreUpdate to look like this...

     @Override
     protected void onPreUpdate() {
         ContactType old = 
ClientBaseAdmin.getTempObjectContext().localObject(this); // 
getTempObjectContext gets a new ObjectContext different from the current 
objects.
System.out.println("******Old=" + old.getDefaultAdminLevel() + ",New=" + 
getDefaultAdminLevel());
         if (getDefaultAdminLevel() < old.getDefaultAdminLevel()) {
            throw new CayenneRuntimeException("Cannot create promote 
ContactType to a higher authority without emptying the group:";
         }
     }

... and generated the expected output of ...

******Old=5,New=2

Relative Simplicity and Success that I can live with although perhaps 
something like this.DEFAULT_ADMIN_LEVEL.getPreUpdateValue() would be 
even simpler if it would be possible to implement within Cayenne.  ;-)



On 2019-01-22 4:48 p.m., Andrus Adamchik wrote:
> There is a big improvement on post-update change capturing API ("cayenne-commitlog" module [1]). However pre-update does require manual inspection of the object vs. saved state.
>
> Andrus
>
> [1] https://cayenne.apache.org/docs/4.0/cayenne-guide/#ext-commit-log
>
>> On Jan 22, 2019, at 4:42 PM, Andrew Willerding <aw...@itsurcom.com> wrote:
>>
>> Thank you Maik.
>>
>> I was hoping for something much simpler than this and that's baked into Cayenne.
>>
>>
>> On 2019-01-22 11:19 a.m., Maik Musall wrote:
>>> Hi Andrew,
>>>
>>> I did it like this in a DataDomainListener:
>>>
>>> 	@PreUpdate( { CayenneDataObject.class } )
>>> 	public void handleBeforeUpdateHook( CayenneDataObject object ) {
>>> 		if( object instanceof BeforeSaveHook ) {
>>> 			((BeforeSaveHook)object).beforeSaveAction();
>>> 		}
>>> 	}
>>>
>>> and then have BeforeSaveHook be an interface, and then implementing beforeSaveAction in the DataObject classes. To get the actual changes, I also didn't find anything handy, and ended up doing it like this (in my custom DataObject subclass):
>>>
>>> 	public Map<String,ValueDifference<Object>> changedAttributes() {
>>> 		Set<String> attributeKeys = new HashSet<>( attributeKeys() );
>>> 		Map<String,Object> dataRow = oc().getObjectStore().getSnapshot( getObjectId() );
>>> 		
>>> 		if( dataRow == null ) {	// newly inserted object
>>> 			dataRow = new HashMap<>();
>>> 			for( String key : attributeKeys ) {
>>> 				dataRow.put( key, null );
>>> 			}
>>> 		}
>>> 		
>>> 		Map<String,Object> committedValues = new HashMap<>();
>>> 		Map<String,Object> uncommittedValues = new HashMap<>();
>>> 		for( String key : dataRow.keySet() ) {
>>> 			if( !attributeKeys.contains( key ) ) continue;
>>> 			committedValues.put( key, dataRow.get( key ) );
>>> 			Object uncommittedValue = readPropertyDirectly( key );
>>> 			uncommittedValues.put( key, uncommittedValue );
>>> 		}
>>> 		
>>> 		MapDifference<String,Object> difference = Maps.difference( committedValues, uncommittedValues );
>>> 		Map<String,ValueDifference<Object>> entriesDiffering = difference.entriesDiffering();	 // assuming the all keys will always be present in both
>>> 		return entriesDiffering;
>>> 	}
>>>
>>>
>>> (where ValueDifference and MapDifference are Guava classes). Hope it helps, perhaps someone else has a better solution.
>>>
>>> Maik
>>>
>>>
>>>
>>>> Am 22.01.2019 um 17:06 schrieb Andrew Willerding <aw...@itsurcom.com>:
>>>>
>>>> I can't seem to find documentation, or better yet some examples for 4.0 or 4.1 that explains how to check for either a single property change or how to get a list of the property changes in an onPreUpdate callback. Unless I'm missing something obvious (entirely likely ;-) ) the Lifecycle events documentation doesn't seem to mention any way to do this.  Is this something that I need to manually track in the application?
>>>>
>>>> Also, what should I do if I want to reject the changes inside of an onPreUpdate callback?  Do I need to do an explicit rollback or simply throw a CayenneRuntimeException of some sort?  I'm guessing it's an explicit rollback.
>>>>
>>>> Thanks,
>>>>
>>>> Andrew
>>>>
>>>>
>>>>

Re: How to get the changes to an object in PreUpdate?

Posted by Andrus Adamchik <an...@objectstyle.org>.
There is a big improvement on post-update change capturing API ("cayenne-commitlog" module [1]). However pre-update does require manual inspection of the object vs. saved state.

Andrus

[1] https://cayenne.apache.org/docs/4.0/cayenne-guide/#ext-commit-log

> On Jan 22, 2019, at 4:42 PM, Andrew Willerding <aw...@itsurcom.com> wrote:
> 
> Thank you Maik.
> 
> I was hoping for something much simpler than this and that's baked into Cayenne.
> 
> 
> On 2019-01-22 11:19 a.m., Maik Musall wrote:
>> Hi Andrew,
>> 
>> I did it like this in a DataDomainListener:
>> 
>> 	@PreUpdate( { CayenneDataObject.class } )
>> 	public void handleBeforeUpdateHook( CayenneDataObject object ) {
>> 		if( object instanceof BeforeSaveHook ) {
>> 			((BeforeSaveHook)object).beforeSaveAction();
>> 		}
>> 	}
>> 
>> and then have BeforeSaveHook be an interface, and then implementing beforeSaveAction in the DataObject classes. To get the actual changes, I also didn't find anything handy, and ended up doing it like this (in my custom DataObject subclass):
>> 
>> 	public Map<String,ValueDifference<Object>> changedAttributes() {
>> 		Set<String> attributeKeys = new HashSet<>( attributeKeys() );
>> 		Map<String,Object> dataRow = oc().getObjectStore().getSnapshot( getObjectId() );
>> 		
>> 		if( dataRow == null ) {	// newly inserted object
>> 			dataRow = new HashMap<>();
>> 			for( String key : attributeKeys ) {
>> 				dataRow.put( key, null );
>> 			}
>> 		}
>> 		
>> 		Map<String,Object> committedValues = new HashMap<>();
>> 		Map<String,Object> uncommittedValues = new HashMap<>();
>> 		for( String key : dataRow.keySet() ) {
>> 			if( !attributeKeys.contains( key ) ) continue;
>> 			committedValues.put( key, dataRow.get( key ) );
>> 			Object uncommittedValue = readPropertyDirectly( key );
>> 			uncommittedValues.put( key, uncommittedValue );
>> 		}
>> 		
>> 		MapDifference<String,Object> difference = Maps.difference( committedValues, uncommittedValues );
>> 		Map<String,ValueDifference<Object>> entriesDiffering = difference.entriesDiffering();	 // assuming the all keys will always be present in both
>> 		return entriesDiffering;
>> 	}
>> 
>> 
>> (where ValueDifference and MapDifference are Guava classes). Hope it helps, perhaps someone else has a better solution.
>> 
>> Maik
>> 
>> 
>> 
>>> Am 22.01.2019 um 17:06 schrieb Andrew Willerding <aw...@itsurcom.com>:
>>> 
>>> I can't seem to find documentation, or better yet some examples for 4.0 or 4.1 that explains how to check for either a single property change or how to get a list of the property changes in an onPreUpdate callback. Unless I'm missing something obvious (entirely likely ;-) ) the Lifecycle events documentation doesn't seem to mention any way to do this.  Is this something that I need to manually track in the application?
>>> 
>>> Also, what should I do if I want to reject the changes inside of an onPreUpdate callback?  Do I need to do an explicit rollback or simply throw a CayenneRuntimeException of some sort?  I'm guessing it's an explicit rollback.
>>> 
>>> Thanks,
>>> 
>>> Andrew
>>> 
>>> 
>>> 
>> 


Re: How to get the changes to an object in PreUpdate?

Posted by Andrew Willerding <aw...@itsurcom.com>.
Thank you Maik.

I was hoping for something much simpler than this and that's baked into 
Cayenne.


On 2019-01-22 11:19 a.m., Maik Musall wrote:
> Hi Andrew,
>
> I did it like this in a DataDomainListener:
>
> 	@PreUpdate( { CayenneDataObject.class } )
> 	public void handleBeforeUpdateHook( CayenneDataObject object ) {
> 		if( object instanceof BeforeSaveHook ) {
> 			((BeforeSaveHook)object).beforeSaveAction();
> 		}
> 	}
>
> and then have BeforeSaveHook be an interface, and then implementing beforeSaveAction in the DataObject classes. To get the actual changes, I also didn't find anything handy, and ended up doing it like this (in my custom DataObject subclass):
>
> 	public Map<String,ValueDifference<Object>> changedAttributes() {
> 		Set<String> attributeKeys = new HashSet<>( attributeKeys() );
> 		Map<String,Object> dataRow = oc().getObjectStore().getSnapshot( getObjectId() );
> 		
> 		if( dataRow == null ) {	// newly inserted object
> 			dataRow = new HashMap<>();
> 			for( String key : attributeKeys ) {
> 				dataRow.put( key, null );
> 			}
> 		}
> 		
> 		Map<String,Object> committedValues = new HashMap<>();
> 		Map<String,Object> uncommittedValues = new HashMap<>();
> 		for( String key : dataRow.keySet() ) {
> 			if( !attributeKeys.contains( key ) ) continue;
> 			committedValues.put( key, dataRow.get( key ) );
> 			Object uncommittedValue = readPropertyDirectly( key );
> 			uncommittedValues.put( key, uncommittedValue );
> 		}
> 		
> 		MapDifference<String,Object> difference = Maps.difference( committedValues, uncommittedValues );
> 		Map<String,ValueDifference<Object>> entriesDiffering = difference.entriesDiffering();	 // assuming the all keys will always be present in both
> 		return entriesDiffering;
> 	}
>
>
> (where ValueDifference and MapDifference are Guava classes). Hope it helps, perhaps someone else has a better solution.
>
> Maik
>
>
>
>> Am 22.01.2019 um 17:06 schrieb Andrew Willerding <aw...@itsurcom.com>:
>>
>> I can't seem to find documentation, or better yet some examples for 4.0 or 4.1 that explains how to check for either a single property change or how to get a list of the property changes in an onPreUpdate callback. Unless I'm missing something obvious (entirely likely ;-) ) the Lifecycle events documentation doesn't seem to mention any way to do this.  Is this something that I need to manually track in the application?
>>
>> Also, what should I do if I want to reject the changes inside of an onPreUpdate callback?  Do I need to do an explicit rollback or simply throw a CayenneRuntimeException of some sort?  I'm guessing it's an explicit rollback.
>>
>> Thanks,
>>
>> Andrew
>>
>>
>>
>

Re: How to get the changes to an object in PreUpdate?

Posted by Maik Musall <ma...@selbstdenker.ag>.
Hi Andrew,

I did it like this in a DataDomainListener:

	@PreUpdate( { CayenneDataObject.class } )
	public void handleBeforeUpdateHook( CayenneDataObject object ) {
		if( object instanceof BeforeSaveHook ) {
			((BeforeSaveHook)object).beforeSaveAction();
		}
	}

and then have BeforeSaveHook be an interface, and then implementing beforeSaveAction in the DataObject classes. To get the actual changes, I also didn't find anything handy, and ended up doing it like this (in my custom DataObject subclass):

	public Map<String,ValueDifference<Object>> changedAttributes() {
		Set<String> attributeKeys = new HashSet<>( attributeKeys() );
		Map<String,Object> dataRow = oc().getObjectStore().getSnapshot( getObjectId() );
		
		if( dataRow == null ) {	// newly inserted object
			dataRow = new HashMap<>();
			for( String key : attributeKeys ) {
				dataRow.put( key, null );
			}
		}
		
		Map<String,Object> committedValues = new HashMap<>();
		Map<String,Object> uncommittedValues = new HashMap<>();
		for( String key : dataRow.keySet() ) {
			if( !attributeKeys.contains( key ) ) continue;
			committedValues.put( key, dataRow.get( key ) );
			Object uncommittedValue = readPropertyDirectly( key );
			uncommittedValues.put( key, uncommittedValue );
		}
		
		MapDifference<String,Object> difference = Maps.difference( committedValues, uncommittedValues );
		Map<String,ValueDifference<Object>> entriesDiffering = difference.entriesDiffering();	 // assuming the all keys will always be present in both
		return entriesDiffering;
	}


(where ValueDifference and MapDifference are Guava classes). Hope it helps, perhaps someone else has a better solution.

Maik



> Am 22.01.2019 um 17:06 schrieb Andrew Willerding <aw...@itsurcom.com>:
> 
> I can't seem to find documentation, or better yet some examples for 4.0 or 4.1 that explains how to check for either a single property change or how to get a list of the property changes in an onPreUpdate callback. Unless I'm missing something obvious (entirely likely ;-) ) the Lifecycle events documentation doesn't seem to mention any way to do this.  Is this something that I need to manually track in the application?
> 
> Also, what should I do if I want to reject the changes inside of an onPreUpdate callback?  Do I need to do an explicit rollback or simply throw a CayenneRuntimeException of some sort?  I'm guessing it's an explicit rollback.
> 
> Thanks,
> 
> Andrew
> 
> 
>