You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Matt R Watson <Ma...@swarmbox.com> on 2018/09/11 00:04:25 UTC

DataObject changes - before commit

Now that the DataObject classes are “field based”, I would like to find a way to replace the heavy Map we have placed on all of our DataObject classes.

We extend Cayenne’s default class and added a HashMap to keep track of the original values (as it changes) since the object has been loaded and before committing. (see our class below)

This allows us to check if something is modified ( inventoryAdjustment.isPropertyModified(“date”), or inventoryAdjustment.getOriginalProperty(“date”) )

I’d like to find a Cayenne friendly way of doing this. Any suggestions?

Thanks,
Matt

----------------------------------------------------


public class CayenneBaseDataObject extends CayenneDataObject {

	/**
	 * Stores the original values for this object's properties. Uses the
	 * property name string as the key for each entry. Its purpose is to keep
	 * track of any original values since the object was initialized or last
	 * committed.
	 */
    private final Map<String,Object> originalPropertyMap = New.map();

	/**
	 * Initializes the originalPropertyMap. Used as a callback during the
	 */
	public void initializeOriginalPropertyMap() {
		originalPropertyMap.clear();
	}

	@Override
    protected void beforePropertyWrite(String propName, Object oldValue, Object newValue) {
		if (!originalPropertyMap.containsKey(propName)) {
			originalPropertyMap.put(propName, oldValue);
		}

		super.beforePropertyWrite(propName, oldValue, newValue);
	}

	/**
	 * Overrides
	 * {@link org.apache.cayenne.DataObject#writeProperty(String, Object)} by
	 * saving a {@link CayenneBaseDataObject}'s existing property value, if any,
	 * to the {@link #originalPropertyMap} prior to updating the
	 * {@link CayenneBaseDataObject}'s property with a new value.
	 *
	 * @param relationship
	 * 		the name of the to-one-relationship
	 * @param value
	 * 		the value used to set the to-one-relationship
	 * @param reverse
	 * 		a boolean indicating whether to set the reverse relationship as well
	 */
	@Override
	public void setToOneTarget(final String relationship, final org.apache.cayenne.DataObject value, final boolean reverse) {
		if (!originalPropertyMap.containsKey(relationship)) {
            originalPropertyMap.put(relationship, this.readProperty(relationship));
		}

		super.setToOneTarget(relationship, value, reverse);
	}

	
	@Override
	public Object getOriginalProperty(final String property) {
		if (!originalPropertyMap.containsKey(property)) {
			return this.readProperty(property);
		}

		return originalPropertyMap.get(property);
	}

	@Override
	public Boolean isPropertyModified(final String property) {
		final Object original = this.getOriginalProperty(property);
		final Object current = this.readProperty(property);

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

}


Re: DataObject changes - before commit

Posted by Matt R Watson <Ma...@swarmbox.com>.
True, thanks for the suggestions John. I agree with your assessment of code costs vs memories costs

> On Sep 12, 2018, at 2:42 PM, John Huss <jo...@gmail.com> wrote:
> 
> Or I suppose you could create fields for each of the original values in the
> same way that the current values are stored.
> 
> On Wed, Sep 12, 2018 at 4:32 PM John Huss <jo...@gmail.com> wrote:
> 
>> Cayenne stores the original values, but they are in DbEntity
>> representation rather than ObjEntity representation which makes comparing
>> the two difficult. Without writing a lot of code to convert and compare
>> your best bet would be to use a more efficient data structure without any
>> extra unneeded space. That would be an array (Object[]) that is smart
>> enough to map a fixed set of string keys into array indexes. To do this
>> you'll need a HashMap<String, Integer> per ObjEntity(table) that maps from
>> string key to array index. Then all of your CayenneDataObjects will need an
>> Object[] with the original values.
>> 
>> On Tue, Sep 11, 2018 at 10:58 AM Matt R Watson <Ma...@swarmbox.com> wrote:
>> 
>>> Thanks mrg.
>>> 
>>> I am aware that Cayenne knows about the original values, but trying to
>>> find an efficient way to access them. Or at least more efficient than the
>>> Map we are currently using, which is heavy on memory yet quick on access
>>> time (and easy to read code). I’ve seen your utilities in the past when
>>> searching for a solution for this, but I don’t see anything thats
>>> comparable to finding the original value of a specific property on a
>>> specific object. I also don’t see where you are grabbing the original value
>>> of a relationship. I know the snapshot can show me the original DataRow
>>> values, but I don’t think those are always the same “type” as the actual
>>> DataObject properties.
>>> 
>>> Still hoping for solution for our current code (that doesn’t have to loop
>>> over all modified objects, every time):
>>> 
>>> inventoryAdjustment.isPropertyModified(“date”)
>>> inventoryAdjustment.getOriginalProperty(“date”)
>>> 
>>> with something like:
>>> 
>>> Cayenne.isPropertyModified(inventoryAdjustment, "date”)
>>> Cayenne.getOriginalProperty(inventoryAdjustment, "date”)
>>> 
>>> Thanks,
>>> Matt
>>> 
>>>> On Sep 11, 2018, at 5:31 AM, Michael Gentry <bl...@gmail.com>
>>> wrote:
>>>> 
>>>> Hi Matt,
>>>> 
>>>> Cayenne already keeps track of the original values for you.  This is
>>> needed
>>>> in order to do optimistic locking, plus it allows Cayenne to only send
>>> over
>>>> changes in a record instead of the entire record when you are doing an
>>>> UPDATE.
>>>> 
>>>> I haven't tried this approach on the field-based version of Cayenne yet,
>>>> but these two utility methods are similar and illustrate how to find the
>>>> underlying values and detect the changes, at least in 3.x:
>>>> 
>>>> 
>>> https://gist.github.com/mrg/4dce22b67175c27f4047#file-cayenneutils-java-L124
>>>> 
>>>> 
>>> https://gist.github.com/mrg/4dce22b67175c27f4047#file-cayenneutils-java-L184
>>>> 
>>>> I believe you could adapt for your purposes/needs pretty readily.
>>>> 
>>>> mrg
>>>> 
>>>> 
>>>> On Mon, Sep 10, 2018 at 8:04 PM Matt R Watson <Ma...@swarmbox.com>
>>> wrote:
>>>> 
>>>>> Now that the DataObject classes are “field based”, I would like to
>>> find a
>>>>> way to replace the heavy Map we have placed on all of our DataObject
>>>>> classes.
>>>>> 
>>>>> We extend Cayenne’s default class and added a HashMap to keep track of
>>> the
>>>>> original values (as it changes) since the object has been loaded and
>>> before
>>>>> committing. (see our class below)
>>>>> 
>>>>> This allows us to check if something is modified (
>>>>> inventoryAdjustment.isPropertyModified(“date”), or
>>>>> inventoryAdjustment.getOriginalProperty(“date”) )
>>>>> 
>>>>> I’d like to find a Cayenne friendly way of doing this. Any suggestions?
>>>>> 
>>>>> Thanks,
>>>>> Matt
>>>>> 
>>>>> ----------------------------------------------------
>>>>> 
>>>>> 
>>>>> public class CayenneBaseDataObject extends CayenneDataObject {
>>>>> 
>>>>>       /**
>>>>>        * Stores the original values for this object's properties. Uses
>>>>> the
>>>>>        * property name string as the key for each entry. Its purpose
>>> is
>>>>> to keep
>>>>>        * track of any original values since the object was initialized
>>>>> or last
>>>>>        * committed.
>>>>>        */
>>>>>   private final Map<String,Object> originalPropertyMap = New.map();
>>>>> 
>>>>>       /**
>>>>>        * Initializes the originalPropertyMap. Used as a callback
>>> during
>>>>> the
>>>>>        */
>>>>>       public void initializeOriginalPropertyMap() {
>>>>>               originalPropertyMap.clear();
>>>>>       }
>>>>> 
>>>>>       @Override
>>>>>   protected void beforePropertyWrite(String propName, Object oldValue,
>>>>> Object newValue) {
>>>>>               if (!originalPropertyMap.containsKey(propName)) {
>>>>>                       originalPropertyMap.put(propName, oldValue);
>>>>>               }
>>>>> 
>>>>>               super.beforePropertyWrite(propName, oldValue, newValue);
>>>>>       }
>>>>> 
>>>>>       /**
>>>>>        * Overrides
>>>>>        * {@link org.apache.cayenne.DataObject#writeProperty(String,
>>>>> Object)} by
>>>>>        * saving a {@link CayenneBaseDataObject}'s existing property
>>>>> value, if any,
>>>>>        * to the {@link #originalPropertyMap} prior to updating the
>>>>>        * {@link CayenneBaseDataObject}'s property with a new value.
>>>>>        *
>>>>>        * @param relationship
>>>>>        *              the name of the to-one-relationship
>>>>>        * @param value
>>>>>        *              the value used to set the to-one-relationship
>>>>>        * @param reverse
>>>>>        *              a boolean indicating whether to set the reverse
>>>>> relationship as well
>>>>>        */
>>>>>       @Override
>>>>>       public void setToOneTarget(final String relationship, final
>>>>> org.apache.cayenne.DataObject value, final boolean reverse) {
>>>>>               if (!originalPropertyMap.containsKey(relationship)) {
>>>>>           originalPropertyMap.put(relationship,
>>>>> this.readProperty(relationship));
>>>>>               }
>>>>> 
>>>>>               super.setToOneTarget(relationship, value, reverse);
>>>>>       }
>>>>> 
>>>>> 
>>>>>       @Override
>>>>>       public Object getOriginalProperty(final String property) {
>>>>>               if (!originalPropertyMap.containsKey(property)) {
>>>>>                       return this.readProperty(property);
>>>>>               }
>>>>> 
>>>>>               return originalPropertyMap.get(property);
>>>>>       }
>>>>> 
>>>>>       @Override
>>>>>       public Boolean isPropertyModified(final String property) {
>>>>>               final Object original =
>>> this.getOriginalProperty(property);
>>>>>               final Object current = this.readProperty(property);
>>>>> 
>>>>>               return !Objects.equals(original, current);
>>>>>       }
>>>>> 
>>>>> }
>>>>> 
>>>>> 
>>> 
>>> 


Re: DataObject changes - before commit

Posted by John Huss <jo...@gmail.com>.
Or I suppose you could create fields for each of the original values in the
same way that the current values are stored.

On Wed, Sep 12, 2018 at 4:32 PM John Huss <jo...@gmail.com> wrote:

> Cayenne stores the original values, but they are in DbEntity
> representation rather than ObjEntity representation which makes comparing
> the two difficult. Without writing a lot of code to convert and compare
> your best bet would be to use a more efficient data structure without any
> extra unneeded space. That would be an array (Object[]) that is smart
> enough to map a fixed set of string keys into array indexes. To do this
> you'll need a HashMap<String, Integer> per ObjEntity(table) that maps from
> string key to array index. Then all of your CayenneDataObjects will need an
> Object[] with the original values.
>
> On Tue, Sep 11, 2018 at 10:58 AM Matt R Watson <Ma...@swarmbox.com> wrote:
>
>> Thanks mrg.
>>
>> I am aware that Cayenne knows about the original values, but trying to
>> find an efficient way to access them. Or at least more efficient than the
>> Map we are currently using, which is heavy on memory yet quick on access
>> time (and easy to read code). I’ve seen your utilities in the past when
>> searching for a solution for this, but I don’t see anything thats
>> comparable to finding the original value of a specific property on a
>> specific object. I also don’t see where you are grabbing the original value
>> of a relationship. I know the snapshot can show me the original DataRow
>> values, but I don’t think those are always the same “type” as the actual
>> DataObject properties.
>>
>> Still hoping for solution for our current code (that doesn’t have to loop
>> over all modified objects, every time):
>>
>> inventoryAdjustment.isPropertyModified(“date”)
>> inventoryAdjustment.getOriginalProperty(“date”)
>>
>> with something like:
>>
>> Cayenne.isPropertyModified(inventoryAdjustment, "date”)
>> Cayenne.getOriginalProperty(inventoryAdjustment, "date”)
>>
>> Thanks,
>> Matt
>>
>> > On Sep 11, 2018, at 5:31 AM, Michael Gentry <bl...@gmail.com>
>> wrote:
>> >
>> > Hi Matt,
>> >
>> > Cayenne already keeps track of the original values for you.  This is
>> needed
>> > in order to do optimistic locking, plus it allows Cayenne to only send
>> over
>> > changes in a record instead of the entire record when you are doing an
>> > UPDATE.
>> >
>> > I haven't tried this approach on the field-based version of Cayenne yet,
>> > but these two utility methods are similar and illustrate how to find the
>> > underlying values and detect the changes, at least in 3.x:
>> >
>> >
>> https://gist.github.com/mrg/4dce22b67175c27f4047#file-cayenneutils-java-L124
>> >
>> >
>> https://gist.github.com/mrg/4dce22b67175c27f4047#file-cayenneutils-java-L184
>> >
>> > I believe you could adapt for your purposes/needs pretty readily.
>> >
>> > mrg
>> >
>> >
>> > On Mon, Sep 10, 2018 at 8:04 PM Matt R Watson <Ma...@swarmbox.com>
>> wrote:
>> >
>> >> Now that the DataObject classes are “field based”, I would like to
>> find a
>> >> way to replace the heavy Map we have placed on all of our DataObject
>> >> classes.
>> >>
>> >> We extend Cayenne’s default class and added a HashMap to keep track of
>> the
>> >> original values (as it changes) since the object has been loaded and
>> before
>> >> committing. (see our class below)
>> >>
>> >> This allows us to check if something is modified (
>> >> inventoryAdjustment.isPropertyModified(“date”), or
>> >> inventoryAdjustment.getOriginalProperty(“date”) )
>> >>
>> >> I’d like to find a Cayenne friendly way of doing this. Any suggestions?
>> >>
>> >> Thanks,
>> >> Matt
>> >>
>> >> ----------------------------------------------------
>> >>
>> >>
>> >> public class CayenneBaseDataObject extends CayenneDataObject {
>> >>
>> >>        /**
>> >>         * Stores the original values for this object's properties. Uses
>> >> the
>> >>         * property name string as the key for each entry. Its purpose
>> is
>> >> to keep
>> >>         * track of any original values since the object was initialized
>> >> or last
>> >>         * committed.
>> >>         */
>> >>    private final Map<String,Object> originalPropertyMap = New.map();
>> >>
>> >>        /**
>> >>         * Initializes the originalPropertyMap. Used as a callback
>> during
>> >> the
>> >>         */
>> >>        public void initializeOriginalPropertyMap() {
>> >>                originalPropertyMap.clear();
>> >>        }
>> >>
>> >>        @Override
>> >>    protected void beforePropertyWrite(String propName, Object oldValue,
>> >> Object newValue) {
>> >>                if (!originalPropertyMap.containsKey(propName)) {
>> >>                        originalPropertyMap.put(propName, oldValue);
>> >>                }
>> >>
>> >>                super.beforePropertyWrite(propName, oldValue, newValue);
>> >>        }
>> >>
>> >>        /**
>> >>         * Overrides
>> >>         * {@link org.apache.cayenne.DataObject#writeProperty(String,
>> >> Object)} by
>> >>         * saving a {@link CayenneBaseDataObject}'s existing property
>> >> value, if any,
>> >>         * to the {@link #originalPropertyMap} prior to updating the
>> >>         * {@link CayenneBaseDataObject}'s property with a new value.
>> >>         *
>> >>         * @param relationship
>> >>         *              the name of the to-one-relationship
>> >>         * @param value
>> >>         *              the value used to set the to-one-relationship
>> >>         * @param reverse
>> >>         *              a boolean indicating whether to set the reverse
>> >> relationship as well
>> >>         */
>> >>        @Override
>> >>        public void setToOneTarget(final String relationship, final
>> >> org.apache.cayenne.DataObject value, final boolean reverse) {
>> >>                if (!originalPropertyMap.containsKey(relationship)) {
>> >>            originalPropertyMap.put(relationship,
>> >> this.readProperty(relationship));
>> >>                }
>> >>
>> >>                super.setToOneTarget(relationship, value, reverse);
>> >>        }
>> >>
>> >>
>> >>        @Override
>> >>        public Object getOriginalProperty(final String property) {
>> >>                if (!originalPropertyMap.containsKey(property)) {
>> >>                        return this.readProperty(property);
>> >>                }
>> >>
>> >>                return originalPropertyMap.get(property);
>> >>        }
>> >>
>> >>        @Override
>> >>        public Boolean isPropertyModified(final String property) {
>> >>                final Object original =
>> this.getOriginalProperty(property);
>> >>                final Object current = this.readProperty(property);
>> >>
>> >>                return !Objects.equals(original, current);
>> >>        }
>> >>
>> >> }
>> >>
>> >>
>>
>>

Re: DataObject changes - before commit

Posted by John Huss <jo...@gmail.com>.
Cayenne stores the original values, but they are in DbEntity representation
rather than ObjEntity representation which makes comparing the two
difficult. Without writing a lot of code to convert and compare your best
bet would be to use a more efficient data structure without any extra
unneeded space. That would be an array (Object[]) that is smart enough to
map a fixed set of string keys into array indexes. To do this you'll need a
HashMap<String, Integer> per ObjEntity(table) that maps from string key to
array index. Then all of your CayenneDataObjects will need an Object[] with
the original values.

On Tue, Sep 11, 2018 at 10:58 AM Matt R Watson <Ma...@swarmbox.com> wrote:

> Thanks mrg.
>
> I am aware that Cayenne knows about the original values, but trying to
> find an efficient way to access them. Or at least more efficient than the
> Map we are currently using, which is heavy on memory yet quick on access
> time (and easy to read code). I’ve seen your utilities in the past when
> searching for a solution for this, but I don’t see anything thats
> comparable to finding the original value of a specific property on a
> specific object. I also don’t see where you are grabbing the original value
> of a relationship. I know the snapshot can show me the original DataRow
> values, but I don’t think those are always the same “type” as the actual
> DataObject properties.
>
> Still hoping for solution for our current code (that doesn’t have to loop
> over all modified objects, every time):
>
> inventoryAdjustment.isPropertyModified(“date”)
> inventoryAdjustment.getOriginalProperty(“date”)
>
> with something like:
>
> Cayenne.isPropertyModified(inventoryAdjustment, "date”)
> Cayenne.getOriginalProperty(inventoryAdjustment, "date”)
>
> Thanks,
> Matt
>
> > On Sep 11, 2018, at 5:31 AM, Michael Gentry <bl...@gmail.com> wrote:
> >
> > Hi Matt,
> >
> > Cayenne already keeps track of the original values for you.  This is
> needed
> > in order to do optimistic locking, plus it allows Cayenne to only send
> over
> > changes in a record instead of the entire record when you are doing an
> > UPDATE.
> >
> > I haven't tried this approach on the field-based version of Cayenne yet,
> > but these two utility methods are similar and illustrate how to find the
> > underlying values and detect the changes, at least in 3.x:
> >
> >
> https://gist.github.com/mrg/4dce22b67175c27f4047#file-cayenneutils-java-L124
> >
> >
> https://gist.github.com/mrg/4dce22b67175c27f4047#file-cayenneutils-java-L184
> >
> > I believe you could adapt for your purposes/needs pretty readily.
> >
> > mrg
> >
> >
> > On Mon, Sep 10, 2018 at 8:04 PM Matt R Watson <Ma...@swarmbox.com> wrote:
> >
> >> Now that the DataObject classes are “field based”, I would like to find
> a
> >> way to replace the heavy Map we have placed on all of our DataObject
> >> classes.
> >>
> >> We extend Cayenne’s default class and added a HashMap to keep track of
> the
> >> original values (as it changes) since the object has been loaded and
> before
> >> committing. (see our class below)
> >>
> >> This allows us to check if something is modified (
> >> inventoryAdjustment.isPropertyModified(“date”), or
> >> inventoryAdjustment.getOriginalProperty(“date”) )
> >>
> >> I’d like to find a Cayenne friendly way of doing this. Any suggestions?
> >>
> >> Thanks,
> >> Matt
> >>
> >> ----------------------------------------------------
> >>
> >>
> >> public class CayenneBaseDataObject extends CayenneDataObject {
> >>
> >>        /**
> >>         * Stores the original values for this object's properties. Uses
> >> the
> >>         * property name string as the key for each entry. Its purpose is
> >> to keep
> >>         * track of any original values since the object was initialized
> >> or last
> >>         * committed.
> >>         */
> >>    private final Map<String,Object> originalPropertyMap = New.map();
> >>
> >>        /**
> >>         * Initializes the originalPropertyMap. Used as a callback during
> >> the
> >>         */
> >>        public void initializeOriginalPropertyMap() {
> >>                originalPropertyMap.clear();
> >>        }
> >>
> >>        @Override
> >>    protected void beforePropertyWrite(String propName, Object oldValue,
> >> Object newValue) {
> >>                if (!originalPropertyMap.containsKey(propName)) {
> >>                        originalPropertyMap.put(propName, oldValue);
> >>                }
> >>
> >>                super.beforePropertyWrite(propName, oldValue, newValue);
> >>        }
> >>
> >>        /**
> >>         * Overrides
> >>         * {@link org.apache.cayenne.DataObject#writeProperty(String,
> >> Object)} by
> >>         * saving a {@link CayenneBaseDataObject}'s existing property
> >> value, if any,
> >>         * to the {@link #originalPropertyMap} prior to updating the
> >>         * {@link CayenneBaseDataObject}'s property with a new value.
> >>         *
> >>         * @param relationship
> >>         *              the name of the to-one-relationship
> >>         * @param value
> >>         *              the value used to set the to-one-relationship
> >>         * @param reverse
> >>         *              a boolean indicating whether to set the reverse
> >> relationship as well
> >>         */
> >>        @Override
> >>        public void setToOneTarget(final String relationship, final
> >> org.apache.cayenne.DataObject value, final boolean reverse) {
> >>                if (!originalPropertyMap.containsKey(relationship)) {
> >>            originalPropertyMap.put(relationship,
> >> this.readProperty(relationship));
> >>                }
> >>
> >>                super.setToOneTarget(relationship, value, reverse);
> >>        }
> >>
> >>
> >>        @Override
> >>        public Object getOriginalProperty(final String property) {
> >>                if (!originalPropertyMap.containsKey(property)) {
> >>                        return this.readProperty(property);
> >>                }
> >>
> >>                return originalPropertyMap.get(property);
> >>        }
> >>
> >>        @Override
> >>        public Boolean isPropertyModified(final String property) {
> >>                final Object original =
> this.getOriginalProperty(property);
> >>                final Object current = this.readProperty(property);
> >>
> >>                return !Objects.equals(original, current);
> >>        }
> >>
> >> }
> >>
> >>
>
>

Re: DataObject changes - before commit

Posted by Matt R Watson <Ma...@swarmbox.com>.
Thanks mrg.

I am aware that Cayenne knows about the original values, but trying to find an efficient way to access them. Or at least more efficient than the Map we are currently using, which is heavy on memory yet quick on access time (and easy to read code). I’ve seen your utilities in the past when searching for a solution for this, but I don’t see anything thats comparable to finding the original value of a specific property on a specific object. I also don’t see where you are grabbing the original value of a relationship. I know the snapshot can show me the original DataRow values, but I don’t think those are always the same “type” as the actual DataObject properties.

Still hoping for solution for our current code (that doesn’t have to loop over all modified objects, every time):

inventoryAdjustment.isPropertyModified(“date”)
inventoryAdjustment.getOriginalProperty(“date”)

with something like:

Cayenne.isPropertyModified(inventoryAdjustment, "date”)
Cayenne.getOriginalProperty(inventoryAdjustment, "date”)

Thanks,
Matt

> On Sep 11, 2018, at 5:31 AM, Michael Gentry <bl...@gmail.com> wrote:
> 
> Hi Matt,
> 
> Cayenne already keeps track of the original values for you.  This is needed
> in order to do optimistic locking, plus it allows Cayenne to only send over
> changes in a record instead of the entire record when you are doing an
> UPDATE.
> 
> I haven't tried this approach on the field-based version of Cayenne yet,
> but these two utility methods are similar and illustrate how to find the
> underlying values and detect the changes, at least in 3.x:
> 
> https://gist.github.com/mrg/4dce22b67175c27f4047#file-cayenneutils-java-L124
> 
> https://gist.github.com/mrg/4dce22b67175c27f4047#file-cayenneutils-java-L184
> 
> I believe you could adapt for your purposes/needs pretty readily.
> 
> mrg
> 
> 
> On Mon, Sep 10, 2018 at 8:04 PM Matt R Watson <Ma...@swarmbox.com> wrote:
> 
>> Now that the DataObject classes are “field based”, I would like to find a
>> way to replace the heavy Map we have placed on all of our DataObject
>> classes.
>> 
>> We extend Cayenne’s default class and added a HashMap to keep track of the
>> original values (as it changes) since the object has been loaded and before
>> committing. (see our class below)
>> 
>> This allows us to check if something is modified (
>> inventoryAdjustment.isPropertyModified(“date”), or
>> inventoryAdjustment.getOriginalProperty(“date”) )
>> 
>> I’d like to find a Cayenne friendly way of doing this. Any suggestions?
>> 
>> Thanks,
>> Matt
>> 
>> ----------------------------------------------------
>> 
>> 
>> public class CayenneBaseDataObject extends CayenneDataObject {
>> 
>>        /**
>>         * Stores the original values for this object's properties. Uses
>> the
>>         * property name string as the key for each entry. Its purpose is
>> to keep
>>         * track of any original values since the object was initialized
>> or last
>>         * committed.
>>         */
>>    private final Map<String,Object> originalPropertyMap = New.map();
>> 
>>        /**
>>         * Initializes the originalPropertyMap. Used as a callback during
>> the
>>         */
>>        public void initializeOriginalPropertyMap() {
>>                originalPropertyMap.clear();
>>        }
>> 
>>        @Override
>>    protected void beforePropertyWrite(String propName, Object oldValue,
>> Object newValue) {
>>                if (!originalPropertyMap.containsKey(propName)) {
>>                        originalPropertyMap.put(propName, oldValue);
>>                }
>> 
>>                super.beforePropertyWrite(propName, oldValue, newValue);
>>        }
>> 
>>        /**
>>         * Overrides
>>         * {@link org.apache.cayenne.DataObject#writeProperty(String,
>> Object)} by
>>         * saving a {@link CayenneBaseDataObject}'s existing property
>> value, if any,
>>         * to the {@link #originalPropertyMap} prior to updating the
>>         * {@link CayenneBaseDataObject}'s property with a new value.
>>         *
>>         * @param relationship
>>         *              the name of the to-one-relationship
>>         * @param value
>>         *              the value used to set the to-one-relationship
>>         * @param reverse
>>         *              a boolean indicating whether to set the reverse
>> relationship as well
>>         */
>>        @Override
>>        public void setToOneTarget(final String relationship, final
>> org.apache.cayenne.DataObject value, final boolean reverse) {
>>                if (!originalPropertyMap.containsKey(relationship)) {
>>            originalPropertyMap.put(relationship,
>> this.readProperty(relationship));
>>                }
>> 
>>                super.setToOneTarget(relationship, value, reverse);
>>        }
>> 
>> 
>>        @Override
>>        public Object getOriginalProperty(final String property) {
>>                if (!originalPropertyMap.containsKey(property)) {
>>                        return this.readProperty(property);
>>                }
>> 
>>                return originalPropertyMap.get(property);
>>        }
>> 
>>        @Override
>>        public Boolean isPropertyModified(final String property) {
>>                final Object original = this.getOriginalProperty(property);
>>                final Object current = this.readProperty(property);
>> 
>>                return !Objects.equals(original, current);
>>        }
>> 
>> }
>> 
>> 


Re: DataObject changes - before commit

Posted by Michael Gentry <bl...@gmail.com>.
Hi Matt,

Cayenne already keeps track of the original values for you.  This is needed
in order to do optimistic locking, plus it allows Cayenne to only send over
changes in a record instead of the entire record when you are doing an
UPDATE.

I haven't tried this approach on the field-based version of Cayenne yet,
but these two utility methods are similar and illustrate how to find the
underlying values and detect the changes, at least in 3.x:

https://gist.github.com/mrg/4dce22b67175c27f4047#file-cayenneutils-java-L124

https://gist.github.com/mrg/4dce22b67175c27f4047#file-cayenneutils-java-L184

I believe you could adapt for your purposes/needs pretty readily.

mrg


On Mon, Sep 10, 2018 at 8:04 PM Matt R Watson <Ma...@swarmbox.com> wrote:

> Now that the DataObject classes are “field based”, I would like to find a
> way to replace the heavy Map we have placed on all of our DataObject
> classes.
>
> We extend Cayenne’s default class and added a HashMap to keep track of the
> original values (as it changes) since the object has been loaded and before
> committing. (see our class below)
>
> This allows us to check if something is modified (
> inventoryAdjustment.isPropertyModified(“date”), or
> inventoryAdjustment.getOriginalProperty(“date”) )
>
> I’d like to find a Cayenne friendly way of doing this. Any suggestions?
>
> Thanks,
> Matt
>
> ----------------------------------------------------
>
>
> public class CayenneBaseDataObject extends CayenneDataObject {
>
>         /**
>          * Stores the original values for this object's properties. Uses
> the
>          * property name string as the key for each entry. Its purpose is
> to keep
>          * track of any original values since the object was initialized
> or last
>          * committed.
>          */
>     private final Map<String,Object> originalPropertyMap = New.map();
>
>         /**
>          * Initializes the originalPropertyMap. Used as a callback during
> the
>          */
>         public void initializeOriginalPropertyMap() {
>                 originalPropertyMap.clear();
>         }
>
>         @Override
>     protected void beforePropertyWrite(String propName, Object oldValue,
> Object newValue) {
>                 if (!originalPropertyMap.containsKey(propName)) {
>                         originalPropertyMap.put(propName, oldValue);
>                 }
>
>                 super.beforePropertyWrite(propName, oldValue, newValue);
>         }
>
>         /**
>          * Overrides
>          * {@link org.apache.cayenne.DataObject#writeProperty(String,
> Object)} by
>          * saving a {@link CayenneBaseDataObject}'s existing property
> value, if any,
>          * to the {@link #originalPropertyMap} prior to updating the
>          * {@link CayenneBaseDataObject}'s property with a new value.
>          *
>          * @param relationship
>          *              the name of the to-one-relationship
>          * @param value
>          *              the value used to set the to-one-relationship
>          * @param reverse
>          *              a boolean indicating whether to set the reverse
> relationship as well
>          */
>         @Override
>         public void setToOneTarget(final String relationship, final
> org.apache.cayenne.DataObject value, final boolean reverse) {
>                 if (!originalPropertyMap.containsKey(relationship)) {
>             originalPropertyMap.put(relationship,
> this.readProperty(relationship));
>                 }
>
>                 super.setToOneTarget(relationship, value, reverse);
>         }
>
>
>         @Override
>         public Object getOriginalProperty(final String property) {
>                 if (!originalPropertyMap.containsKey(property)) {
>                         return this.readProperty(property);
>                 }
>
>                 return originalPropertyMap.get(property);
>         }
>
>         @Override
>         public Boolean isPropertyModified(final String property) {
>                 final Object original = this.getOriginalProperty(property);
>                 final Object current = this.readProperty(property);
>
>                 return !Objects.equals(original, current);
>         }
>
> }
>
>