You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Tomi NA <he...@gmail.com> on 2006/06/19 10:12:50 UTC

Re: modifying relationship list

On 6/19/06, Marcin Skladaniec <ma...@ish.com.au> wrote:
> Hello
> Just run into interesting cayenne feature.
>
> This code:
>
> rooms = site.getRooms();
> rooms.remove(aRoom);
>
> would alter the relationship
>
> so aRoom.getSite() is now null
>
> I'm wondering if this is a desired effect ?
> This behavior might cause bugs. When someone actually puts code to
> know the fact of relationship being changed (ie. put code into Room
> setSite() and Site add/removeFromRooms()/setRooms() methods ) he
> might be disappointed, as those methods would not run, but the
> relationship will change...

I'm wrestling with this issue myself: I've extended the basic
templates so that events are fired on setter calls, but this practice
has the exact shortcomings you pointed out.
Is there a very good reason why cayenne objects don't fire events on a
lower level (circumventing this problem) out of the box?
Alternatively, if I expand my object code generation templates further
so that objectA.removeFrom(objectB) fires a property change event for
it's objectA.getBArray() as well as objectB.getToA() - will this
completely solve the problem?

t.n.a.

Re: modifying relationship list

Posted by Tomi NA <he...@gmail.com>.
On 6/19/06, Andrus Adamchik <an...@objectstyle.org> wrote:
> Actually Cayenne allows you to easily implement a generic event
> mechanism. IMO this is much more powerful than regular Java Beans
> approach, although requires some understanding of how things work.
>
> In "cayenne classic" (DataContext/DataObject) you can implement a
> generic event mechanism in one place in a superclass that works
> consistently for all objects. You do that by overriding methods in
> DataObject interface (writeProperty). No need to put your code in the
> individual setters. It is up to you how you fire the events (you can
> fire two complimentary events on relationship change if you want).

Are you saying that this would be sufficient i.e. it'd fire the
expected events in the problematic case Martin pointed out?

    public void writeProperty(String propName, Object val) {
        Object oldVal = readProperty(propName);
        super.writeProperty(propName, val);
        firePropertyChangeEvent(propName, oldVal, val);
    }


> Another way that works in both DataContext and CayenneContext and
> will likely become a default way after 1.2, is overriding
> ObjectContext.propertyChanged(..) method to fire the events. This
> allows to track all events by context in addition to tracking events
> of individual objects. IIRC there are limitations using this with
> DataContext, but it should fully work in CayenneContext.
>
> In other words tracking object changes is one of the hidden strengths
> of Cayenne. I wish it wasn't so hidden, so if anyone volunteers to do
> a tutorial or an example based on real experience, you'll be very
> welcome.

Wish I had the knowledge to put together such a tutorial. :)
If anyone here binds cayenne objects via jgoodies binding we could
exchange experiences, try to write a coherent two-page tutorial and
leave it to you to add the "coherent" part to the "two-page" part. :)

t.n.a.

t.n.a.

Re: modifying relationship list

Posted by Andrus Adamchik <an...@objectstyle.org>.
Actually Cayenne allows you to easily implement a generic event  
mechanism. IMO this is much more powerful than regular Java Beans  
approach, although requires some understanding of how things work.

In "cayenne classic" (DataContext/DataObject) you can implement a  
generic event mechanism in one place in a superclass that works  
consistently for all objects. You do that by overriding methods in  
DataObject interface (writeProperty). No need to put your code in the  
individual setters. It is up to you how you fire the events (you can  
fire two complimentary events on relationship change if you want).

Another way that works in both DataContext and CayenneContext and  
will likely become a default way after 1.2, is overriding  
ObjectContext.propertyChanged(..) method to fire the events. This  
allows to track all events by context in addition to tracking events  
of individual objects. IIRC there are limitations using this with  
DataContext, but it should fully work in CayenneContext.

In other words tracking object changes is one of the hidden strengths  
of Cayenne. I wish it wasn't so hidden, so if anyone volunteers to do  
a tutorial or an example based on real experience, you'll be very  
welcome.

Andrus

On Jun 19, 2006, at 12:12 PM, Tomi NA wrote:

> I'm wrestling with this issue myself: I've extended the basic
> templates so that events are fired on setter calls, but this practice
> has the exact shortcomings you pointed out.
> Is there a very good reason why cayenne objects don't fire events on a
> lower level (circumventing this problem) out of the box?
> Alternatively, if I expand my object code generation templates further
> so that objectA.removeFrom(objectB) fires a property change event for
> it's objectA.getBArray() as well as objectB.getToA() - will this
> completely solve the problem?


Re: modifying relationship list

Posted by Andrus Adamchik <an...@objectstyle.org>.
> Monstrous?

In Objective C you'd read a method name with a list of arguments  
almost as a sentence in a human language. So it was verbose, but  
readable. I was referring to its Java reincarnation - when  
concatenated in one word it looks horrible and almost impossible to  
type correctly for such impaired keyboard users like myself :-)

Of course my purpose wasn't EOF bashing, but rather pointing to the  
importance of the automated reverse relationship management :-)

Andrus


On Jun 20, 2006, at 5:04 PM, Gentry, Michael ((Contractor)) wrote:

> Monstrous?  That was my favorite method name!  It stated precisely  
> what
> it was doing, too.  :-)  We hardly ever removed anything, so never got
> to use removeObjectFromBothSidesOfRelationshipWithKey all that much.
> Sigh.
>
> /dev/mrg
>
> PS. For me, it was actually  
> addObject:toBothSidesOfRelationshipWithKey:
> ... Much better.
>
>
>
> -----Original Message-----
> From: Andrus Adamchik [mailto:andrus@objectstyle.org]
> Sent: Tuesday, June 20, 2006 2:19 AM
> To: cayenne-user@incubator.apache.org
> Subject: Re: modifying relationship list
>
>
>>
>> "Someone" should write a paper.
>
> Yep :-)
>
> I recall back in my WebObjects/EOF days, instead of using property
> setters I always used a generic method with monstrous name of the
> Objective C heritage - "addObjectToBothSidesOfRelationshipWithKey"
> and its counterpart "removeObjectFromBothSidesOfRelationshipWithKey"
> exactly because it helped with graph consistency.
>
> So bidirectional relationship management was one of the first
> features in the early Cayenne to address that.
>
> Andrus
>
>
> On Jun 19, 2006, at 8:51 PM, Craig L Russell wrote:
>
>> Hi,
>>
>> This relationship change issue is a very old one in object modeling
>> and made even more interesting when mapping to a relational
>> database, where typically there is only one database column value
>> that represents both sides of the relationship.
>>
>> Among the standards for persistence (J2EE CMP, JDO 1, JDO 2, and
>> EJB3) the requirements are all over the map, with little to guide  
>> you.
>>
>> CMP defines the behavior as I understand Cayenne currently
>> implements it. That is, the relationship on the other side is
>> silently changed to be consistent.
>>
>> JDO 1 is silent on the issue.
>>
>> JDO 2 defines the behavior as "undefined until commit or flush", at
>> which point the relationships on both sides are silently changed to
>> be consistent.
>>
>> EJB3 is silent, and allows relationships to be inconsistent after
>> commit.
>>
>> I believe it is tricky to code defensively if you want to manage
>> relationships in memory. The issue is the possibility of updating
>> the relationship from either side. The apparently straightforward
>> technique is to implement the Room.setSite method to call
>> oldRoom.remove(this) and newRoom.add(this). And the Site.remove
>> method to call theRoom.setSite(null) and Site.add method to call
>> theRoom.setSite(this). But this causes recursion, unless you use
>> special add, remove, and set methods, that need to be protected
>> from public callers. That is, define package protected methods
>> uncoordinatedAdd, uncoordinatedRemove, and uncoordinatedSet that
>> don't manage the other side, but are called from within the public-
>> visible implementations of add, remove, and set. But clearly this
>> is a lot of work for developers, so it's nice that the persistence
>> implementation does some of the hard work for you.
>>
>> "Someone" should write a paper.
>>
>> Craig
>>
>> On Jun 19, 2006, at 1:12 AM, Tomi NA wrote:
>>
>>> On 6/19/06, Marcin Skladaniec <ma...@ish.com.au> wrote:
>>>> Hello
>>>> Just run into interesting cayenne feature.
>>>>
>>>> This code:
>>>>
>>>> rooms = site.getRooms();
>>>> rooms.remove(aRoom);
>>>>
>>>> would alter the relationship
>>>>
>>>> so aRoom.getSite() is now null
>>>>
>>>> I'm wondering if this is a desired effect ?
>>>> This behavior might cause bugs. When someone actually puts code to
>>>> know the fact of relationship being changed (ie. put code into Room
>>>> setSite() and Site add/removeFromRooms()/setRooms() methods ) he
>>>> might be disappointed, as those methods would not run, but the
>>>> relationship will change...
>>>
>>> I'm wrestling with this issue myself: I've extended the basic
>>> templates so that events are fired on setter calls, but this  
>>> practice
>>> has the exact shortcomings you pointed out.
>>> Is there a very good reason why cayenne objects don't fire events
>>> on a
>>> lower level (circumventing this problem) out of the box?
>>> Alternatively, if I expand my object code generation templates
>>> further
>>> so that objectA.removeFrom(objectB) fires a property change event  
>>> for
>>> it's objectA.getBArray() as well as objectB.getToA() - will this
>>> completely solve the problem?
>>>
>>> t.n.a.
>>
>> Craig Russell
>> Architect, Sun Java Enterprise System http://java.sun.com/products/ 
>> jdo
>> 408 276-5638 mailto:Craig.Russell@sun.com
>> P.S. A good JDO? O, Gasp!


RE: modifying relationship list

Posted by "Gentry, Michael (Contractor)" <mi...@fanniemae.com>.
I think we drifted off a bit.  The
removeObjectFromBothSidesOfRelationshipWithKey() method is in EOF, not
in Cayenne.  Cayenne uses a cover method which provides stronger typing
over another method which uses weaker typing.  You could use the weaker
one if you wanted.  If you look at your _ classes, down near the bottom
where Cayenne generates the relationship methods, you'll see the covers.

For to-one relationships:

setToOneTarget(String, Object, true)

For to-many relationships:

addToManyTarget(String, Object, true)
removeToManyTarget(String, Object, true)


The String is the relationship key you defined for use in Cayenne
Modeler.  So, for a Loan record, you might have:

public void addToLoans(Loan obj)
{
  addToManyTarget("loans", obj, true);
}
public void removeFromLoans(Loan obj)
{
  removeToManyTarget("loans", obj, true);
}

Regardless of if you use the stronger or weaker typed method, it still
updates both sides of the relationship automatically.  There is no
functional difference in the way the two operate.  One simply provides
stronger data typing (the "external" method) compared to the other (the
"internal" method).

/dev/mrg



-----Original Message-----
From: Craig.Russell@Sun.COM [mailto:Craig.Russell@Sun.COM] 
Sent: Tuesday, June 20, 2006 12:25 PM
To: cayenne-user@incubator.apache.org
Subject: Re: modifying relationship list


I hate to be a curmudgeon, but it sounds like this might go in the  
wrong direction, if I understand correctly. You need two sets of  
functionality: one that the user sees and transparently updates both  
sides of the relationship; the other, internal, that is called by the  
external methods and individually updates only one side. Many users  
would see void add(Room theRoom) and expect to call it instead of  
void removeObjectFromBothSidesOfRelationshipWithKey(Room theRoom,  
Site theSite). Or perhaps it's really static void  
RelationshipHelper.removeObjectFromBothSidesOfRelationshipWithKey 
(Object oneSide, Object theOtherSide) which can be called from any  
context and has to be omniscient.

If the removeObjectFromBothSidesOfRelationshipWithKey is intended to  
be the user-visible method, then it's very descriptive. Is this the  
user-visible method?

By the way, this is one case where I really miss the "friend"  
annotation of C++. You don't want the internal methods to be visible  
to users, just to the classes on the other side of the relationship.  
Nothing like this semantic exists for Java. :-(

Craig

On Jun 20, 2006, at 6:04 AM, Gentry, Michael (Contractor) wrote:

> Monstrous?  That was my favorite method name!  It stated precisely  
> what
> it was doing, too.  :-)  We hardly ever removed anything, so never got
> to use removeObjectFromBothSidesOfRelationshipWithKey all that much.
> Sigh.
>
> /dev/mrg
>
> PS. For me, it was actually  
> addObject:toBothSidesOfRelationshipWithKey:
> ... Much better.
>
>
>
> -----Original Message-----
> From: Andrus Adamchik [mailto:andrus@objectstyle.org]
> Sent: Tuesday, June 20, 2006 2:19 AM
> To: cayenne-user@incubator.apache.org
> Subject: Re: modifying relationship list
>
>
>>
>> "Someone" should write a paper.
>
> Yep :-)
>
> I recall back in my WebObjects/EOF days, instead of using property
> setters I always used a generic method with monstrous name of the
> Objective C heritage - "addObjectToBothSidesOfRelationshipWithKey"
> and its counterpart "removeObjectFromBothSidesOfRelationshipWithKey"
> exactly because it helped with graph consistency.
>
> So bidirectional relationship management was one of the first
> features in the early Cayenne to address that.
>
> Andrus
>
>
> On Jun 19, 2006, at 8:51 PM, Craig L Russell wrote:
>
>> Hi,
>>
>> This relationship change issue is a very old one in object modeling
>> and made even more interesting when mapping to a relational
>> database, where typically there is only one database column value
>> that represents both sides of the relationship.
>>
>> Among the standards for persistence (J2EE CMP, JDO 1, JDO 2, and
>> EJB3) the requirements are all over the map, with little to guide  
>> you.
>>
>> CMP defines the behavior as I understand Cayenne currently
>> implements it. That is, the relationship on the other side is
>> silently changed to be consistent.
>>
>> JDO 1 is silent on the issue.
>>
>> JDO 2 defines the behavior as "undefined until commit or flush", at
>> which point the relationships on both sides are silently changed to
>> be consistent.
>>
>> EJB3 is silent, and allows relationships to be inconsistent after
>> commit.
>>
>> I believe it is tricky to code defensively if you want to manage
>> relationships in memory. The issue is the possibility of updating
>> the relationship from either side. The apparently straightforward
>> technique is to implement the Room.setSite method to call
>> oldRoom.remove(this) and newRoom.add(this). And the Site.remove
>> method to call theRoom.setSite(null) and Site.add method to call
>> theRoom.setSite(this). But this causes recursion, unless you use
>> special add, remove, and set methods, that need to be protected
>> from public callers. That is, define package protected methods
>> uncoordinatedAdd, uncoordinatedRemove, and uncoordinatedSet that
>> don't manage the other side, but are called from within the public-
>> visible implementations of add, remove, and set. But clearly this
>> is a lot of work for developers, so it's nice that the persistence
>> implementation does some of the hard work for you.
>>
>> "Someone" should write a paper.
>>
>> Craig
>>
>> On Jun 19, 2006, at 1:12 AM, Tomi NA wrote:
>>
>>> On 6/19/06, Marcin Skladaniec <ma...@ish.com.au> wrote:
>>>> Hello
>>>> Just run into interesting cayenne feature.
>>>>
>>>> This code:
>>>>
>>>> rooms = site.getRooms();
>>>> rooms.remove(aRoom);
>>>>
>>>> would alter the relationship
>>>>
>>>> so aRoom.getSite() is now null
>>>>
>>>> I'm wondering if this is a desired effect ?
>>>> This behavior might cause bugs. When someone actually puts code to
>>>> know the fact of relationship being changed (ie. put code into Room
>>>> setSite() and Site add/removeFromRooms()/setRooms() methods ) he
>>>> might be disappointed, as those methods would not run, but the
>>>> relationship will change...
>>>
>>> I'm wrestling with this issue myself: I've extended the basic
>>> templates so that events are fired on setter calls, but this  
>>> practice
>>> has the exact shortcomings you pointed out.
>>> Is there a very good reason why cayenne objects don't fire events
>>> on a
>>> lower level (circumventing this problem) out of the box?
>>> Alternatively, if I expand my object code generation templates
>>> further
>>> so that objectA.removeFrom(objectB) fires a property change event  
>>> for
>>> it's objectA.getBArray() as well as objectB.getToA() - will this
>>> completely solve the problem?
>>>
>>> t.n.a.
>>
>> Craig Russell
>> Architect, Sun Java Enterprise System http://java.sun.com/products/ 
>> jdo
>> 408 276-5638 mailto:Craig.Russell@sun.com
>> P.S. A good JDO? O, Gasp!
>>
>

Craig Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:Craig.Russell@sun.com
P.S. A good JDO? O, Gasp!


Re: modifying relationship list

Posted by Craig L Russell <Cr...@Sun.COM>.
I hate to be a curmudgeon, but it sounds like this might go in the  
wrong direction, if I understand correctly. You need two sets of  
functionality: one that the user sees and transparently updates both  
sides of the relationship; the other, internal, that is called by the  
external methods and individually updates only one side. Many users  
would see void add(Room theRoom) and expect to call it instead of  
void removeObjectFromBothSidesOfRelationshipWithKey(Room theRoom,  
Site theSite). Or perhaps it's really static void  
RelationshipHelper.removeObjectFromBothSidesOfRelationshipWithKey 
(Object oneSide, Object theOtherSide) which can be called from any  
context and has to be omniscient.

If the removeObjectFromBothSidesOfRelationshipWithKey is intended to  
be the user-visible method, then it's very descriptive. Is this the  
user-visible method?

By the way, this is one case where I really miss the "friend"  
annotation of C++. You don't want the internal methods to be visible  
to users, just to the classes on the other side of the relationship.  
Nothing like this semantic exists for Java. :-(

Craig

On Jun 20, 2006, at 6:04 AM, Gentry, Michael (Contractor) wrote:

> Monstrous?  That was my favorite method name!  It stated precisely  
> what
> it was doing, too.  :-)  We hardly ever removed anything, so never got
> to use removeObjectFromBothSidesOfRelationshipWithKey all that much.
> Sigh.
>
> /dev/mrg
>
> PS. For me, it was actually  
> addObject:toBothSidesOfRelationshipWithKey:
> ... Much better.
>
>
>
> -----Original Message-----
> From: Andrus Adamchik [mailto:andrus@objectstyle.org]
> Sent: Tuesday, June 20, 2006 2:19 AM
> To: cayenne-user@incubator.apache.org
> Subject: Re: modifying relationship list
>
>
>>
>> "Someone" should write a paper.
>
> Yep :-)
>
> I recall back in my WebObjects/EOF days, instead of using property
> setters I always used a generic method with monstrous name of the
> Objective C heritage - "addObjectToBothSidesOfRelationshipWithKey"
> and its counterpart "removeObjectFromBothSidesOfRelationshipWithKey"
> exactly because it helped with graph consistency.
>
> So bidirectional relationship management was one of the first
> features in the early Cayenne to address that.
>
> Andrus
>
>
> On Jun 19, 2006, at 8:51 PM, Craig L Russell wrote:
>
>> Hi,
>>
>> This relationship change issue is a very old one in object modeling
>> and made even more interesting when mapping to a relational
>> database, where typically there is only one database column value
>> that represents both sides of the relationship.
>>
>> Among the standards for persistence (J2EE CMP, JDO 1, JDO 2, and
>> EJB3) the requirements are all over the map, with little to guide  
>> you.
>>
>> CMP defines the behavior as I understand Cayenne currently
>> implements it. That is, the relationship on the other side is
>> silently changed to be consistent.
>>
>> JDO 1 is silent on the issue.
>>
>> JDO 2 defines the behavior as "undefined until commit or flush", at
>> which point the relationships on both sides are silently changed to
>> be consistent.
>>
>> EJB3 is silent, and allows relationships to be inconsistent after
>> commit.
>>
>> I believe it is tricky to code defensively if you want to manage
>> relationships in memory. The issue is the possibility of updating
>> the relationship from either side. The apparently straightforward
>> technique is to implement the Room.setSite method to call
>> oldRoom.remove(this) and newRoom.add(this). And the Site.remove
>> method to call theRoom.setSite(null) and Site.add method to call
>> theRoom.setSite(this). But this causes recursion, unless you use
>> special add, remove, and set methods, that need to be protected
>> from public callers. That is, define package protected methods
>> uncoordinatedAdd, uncoordinatedRemove, and uncoordinatedSet that
>> don't manage the other side, but are called from within the public-
>> visible implementations of add, remove, and set. But clearly this
>> is a lot of work for developers, so it's nice that the persistence
>> implementation does some of the hard work for you.
>>
>> "Someone" should write a paper.
>>
>> Craig
>>
>> On Jun 19, 2006, at 1:12 AM, Tomi NA wrote:
>>
>>> On 6/19/06, Marcin Skladaniec <ma...@ish.com.au> wrote:
>>>> Hello
>>>> Just run into interesting cayenne feature.
>>>>
>>>> This code:
>>>>
>>>> rooms = site.getRooms();
>>>> rooms.remove(aRoom);
>>>>
>>>> would alter the relationship
>>>>
>>>> so aRoom.getSite() is now null
>>>>
>>>> I'm wondering if this is a desired effect ?
>>>> This behavior might cause bugs. When someone actually puts code to
>>>> know the fact of relationship being changed (ie. put code into Room
>>>> setSite() and Site add/removeFromRooms()/setRooms() methods ) he
>>>> might be disappointed, as those methods would not run, but the
>>>> relationship will change...
>>>
>>> I'm wrestling with this issue myself: I've extended the basic
>>> templates so that events are fired on setter calls, but this  
>>> practice
>>> has the exact shortcomings you pointed out.
>>> Is there a very good reason why cayenne objects don't fire events
>>> on a
>>> lower level (circumventing this problem) out of the box?
>>> Alternatively, if I expand my object code generation templates
>>> further
>>> so that objectA.removeFrom(objectB) fires a property change event  
>>> for
>>> it's objectA.getBArray() as well as objectB.getToA() - will this
>>> completely solve the problem?
>>>
>>> t.n.a.
>>
>> Craig Russell
>> Architect, Sun Java Enterprise System http://java.sun.com/products/ 
>> jdo
>> 408 276-5638 mailto:Craig.Russell@sun.com
>> P.S. A good JDO? O, Gasp!
>>
>

Craig Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:Craig.Russell@sun.com
P.S. A good JDO? O, Gasp!


RE: modifying relationship list

Posted by "Gentry, Michael (Contractor)" <mi...@fanniemae.com>.
Monstrous?  That was my favorite method name!  It stated precisely what
it was doing, too.  :-)  We hardly ever removed anything, so never got
to use removeObjectFromBothSidesOfRelationshipWithKey all that much.
Sigh.

/dev/mrg

PS. For me, it was actually addObject:toBothSidesOfRelationshipWithKey:
... Much better.



-----Original Message-----
From: Andrus Adamchik [mailto:andrus@objectstyle.org] 
Sent: Tuesday, June 20, 2006 2:19 AM
To: cayenne-user@incubator.apache.org
Subject: Re: modifying relationship list


>
> "Someone" should write a paper.

Yep :-)

I recall back in my WebObjects/EOF days, instead of using property  
setters I always used a generic method with monstrous name of the  
Objective C heritage - "addObjectToBothSidesOfRelationshipWithKey"  
and its counterpart "removeObjectFromBothSidesOfRelationshipWithKey"  
exactly because it helped with graph consistency.

So bidirectional relationship management was one of the first  
features in the early Cayenne to address that.

Andrus


On Jun 19, 2006, at 8:51 PM, Craig L Russell wrote:

> Hi,
>
> This relationship change issue is a very old one in object modeling  
> and made even more interesting when mapping to a relational  
> database, where typically there is only one database column value  
> that represents both sides of the relationship.
>
> Among the standards for persistence (J2EE CMP, JDO 1, JDO 2, and  
> EJB3) the requirements are all over the map, with little to guide you.
>
> CMP defines the behavior as I understand Cayenne currently  
> implements it. That is, the relationship on the other side is  
> silently changed to be consistent.
>
> JDO 1 is silent on the issue.
>
> JDO 2 defines the behavior as "undefined until commit or flush", at  
> which point the relationships on both sides are silently changed to  
> be consistent.
>
> EJB3 is silent, and allows relationships to be inconsistent after  
> commit.
>
> I believe it is tricky to code defensively if you want to manage  
> relationships in memory. The issue is the possibility of updating  
> the relationship from either side. The apparently straightforward  
> technique is to implement the Room.setSite method to call  
> oldRoom.remove(this) and newRoom.add(this). And the Site.remove  
> method to call theRoom.setSite(null) and Site.add method to call  
> theRoom.setSite(this). But this causes recursion, unless you use  
> special add, remove, and set methods, that need to be protected  
> from public callers. That is, define package protected methods  
> uncoordinatedAdd, uncoordinatedRemove, and uncoordinatedSet that  
> don't manage the other side, but are called from within the public- 
> visible implementations of add, remove, and set. But clearly this  
> is a lot of work for developers, so it's nice that the persistence  
> implementation does some of the hard work for you.
>
> "Someone" should write a paper.
>
> Craig
>
> On Jun 19, 2006, at 1:12 AM, Tomi NA wrote:
>
>> On 6/19/06, Marcin Skladaniec <ma...@ish.com.au> wrote:
>>> Hello
>>> Just run into interesting cayenne feature.
>>>
>>> This code:
>>>
>>> rooms = site.getRooms();
>>> rooms.remove(aRoom);
>>>
>>> would alter the relationship
>>>
>>> so aRoom.getSite() is now null
>>>
>>> I'm wondering if this is a desired effect ?
>>> This behavior might cause bugs. When someone actually puts code to
>>> know the fact of relationship being changed (ie. put code into Room
>>> setSite() and Site add/removeFromRooms()/setRooms() methods ) he
>>> might be disappointed, as those methods would not run, but the
>>> relationship will change...
>>
>> I'm wrestling with this issue myself: I've extended the basic
>> templates so that events are fired on setter calls, but this practice
>> has the exact shortcomings you pointed out.
>> Is there a very good reason why cayenne objects don't fire events  
>> on a
>> lower level (circumventing this problem) out of the box?
>> Alternatively, if I expand my object code generation templates  
>> further
>> so that objectA.removeFrom(objectB) fires a property change event for
>> it's objectA.getBArray() as well as objectB.getToA() - will this
>> completely solve the problem?
>>
>> t.n.a.
>
> Craig Russell
> Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
> 408 276-5638 mailto:Craig.Russell@sun.com
> P.S. A good JDO? O, Gasp!
>


Re: modifying relationship list

Posted by Andrus Adamchik <an...@objectstyle.org>.
>
> "Someone" should write a paper.

Yep :-)

I recall back in my WebObjects/EOF days, instead of using property  
setters I always used a generic method with monstrous name of the  
Objective C heritage - "addObjectToBothSidesOfRelationshipWithKey"  
and its counterpart "removeObjectFromBothSidesOfRelationshipWithKey"  
exactly because it helped with graph consistency.

So bidirectional relationship management was one of the first  
features in the early Cayenne to address that.

Andrus


On Jun 19, 2006, at 8:51 PM, Craig L Russell wrote:

> Hi,
>
> This relationship change issue is a very old one in object modeling  
> and made even more interesting when mapping to a relational  
> database, where typically there is only one database column value  
> that represents both sides of the relationship.
>
> Among the standards for persistence (J2EE CMP, JDO 1, JDO 2, and  
> EJB3) the requirements are all over the map, with little to guide you.
>
> CMP defines the behavior as I understand Cayenne currently  
> implements it. That is, the relationship on the other side is  
> silently changed to be consistent.
>
> JDO 1 is silent on the issue.
>
> JDO 2 defines the behavior as "undefined until commit or flush", at  
> which point the relationships on both sides are silently changed to  
> be consistent.
>
> EJB3 is silent, and allows relationships to be inconsistent after  
> commit.
>
> I believe it is tricky to code defensively if you want to manage  
> relationships in memory. The issue is the possibility of updating  
> the relationship from either side. The apparently straightforward  
> technique is to implement the Room.setSite method to call  
> oldRoom.remove(this) and newRoom.add(this). And the Site.remove  
> method to call theRoom.setSite(null) and Site.add method to call  
> theRoom.setSite(this). But this causes recursion, unless you use  
> special add, remove, and set methods, that need to be protected  
> from public callers. That is, define package protected methods  
> uncoordinatedAdd, uncoordinatedRemove, and uncoordinatedSet that  
> don't manage the other side, but are called from within the public- 
> visible implementations of add, remove, and set. But clearly this  
> is a lot of work for developers, so it's nice that the persistence  
> implementation does some of the hard work for you.
>
> "Someone" should write a paper.
>
> Craig
>
> On Jun 19, 2006, at 1:12 AM, Tomi NA wrote:
>
>> On 6/19/06, Marcin Skladaniec <ma...@ish.com.au> wrote:
>>> Hello
>>> Just run into interesting cayenne feature.
>>>
>>> This code:
>>>
>>> rooms = site.getRooms();
>>> rooms.remove(aRoom);
>>>
>>> would alter the relationship
>>>
>>> so aRoom.getSite() is now null
>>>
>>> I'm wondering if this is a desired effect ?
>>> This behavior might cause bugs. When someone actually puts code to
>>> know the fact of relationship being changed (ie. put code into Room
>>> setSite() and Site add/removeFromRooms()/setRooms() methods ) he
>>> might be disappointed, as those methods would not run, but the
>>> relationship will change...
>>
>> I'm wrestling with this issue myself: I've extended the basic
>> templates so that events are fired on setter calls, but this practice
>> has the exact shortcomings you pointed out.
>> Is there a very good reason why cayenne objects don't fire events  
>> on a
>> lower level (circumventing this problem) out of the box?
>> Alternatively, if I expand my object code generation templates  
>> further
>> so that objectA.removeFrom(objectB) fires a property change event for
>> it's objectA.getBArray() as well as objectB.getToA() - will this
>> completely solve the problem?
>>
>> t.n.a.
>
> Craig Russell
> Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
> 408 276-5638 mailto:Craig.Russell@sun.com
> P.S. A good JDO? O, Gasp!
>


Re: modifying relationship list

Posted by Craig L Russell <Cr...@Sun.COM>.
Hi,

This relationship change issue is a very old one in object modeling  
and made even more interesting when mapping to a relational database,  
where typically there is only one database column value that  
represents both sides of the relationship.

Among the standards for persistence (J2EE CMP, JDO 1, JDO 2, and  
EJB3) the requirements are all over the map, with little to guide you.

CMP defines the behavior as I understand Cayenne currently implements  
it. That is, the relationship on the other side is silently changed  
to be consistent.

JDO 1 is silent on the issue.

JDO 2 defines the behavior as "undefined until commit or flush", at  
which point the relationships on both sides are silently changed to  
be consistent.

EJB3 is silent, and allows relationships to be inconsistent after  
commit.

I believe it is tricky to code defensively if you want to manage  
relationships in memory. The issue is the possibility of updating the  
relationship from either side. The apparently straightforward  
technique is to implement the Room.setSite method to call  
oldRoom.remove(this) and newRoom.add(this). And the Site.remove  
method to call theRoom.setSite(null) and Site.add method to call  
theRoom.setSite(this). But this causes recursion, unless you use  
special add, remove, and set methods, that need to be protected from  
public callers. That is, define package protected methods  
uncoordinatedAdd, uncoordinatedRemove, and uncoordinatedSet that  
don't manage the other side, but are called from within the public- 
visible implementations of add, remove, and set. But clearly this is  
a lot of work for developers, so it's nice that the persistence  
implementation does some of the hard work for you.

"Someone" should write a paper.

Craig

On Jun 19, 2006, at 1:12 AM, Tomi NA wrote:

> On 6/19/06, Marcin Skladaniec <ma...@ish.com.au> wrote:
>> Hello
>> Just run into interesting cayenne feature.
>>
>> This code:
>>
>> rooms = site.getRooms();
>> rooms.remove(aRoom);
>>
>> would alter the relationship
>>
>> so aRoom.getSite() is now null
>>
>> I'm wondering if this is a desired effect ?
>> This behavior might cause bugs. When someone actually puts code to
>> know the fact of relationship being changed (ie. put code into Room
>> setSite() and Site add/removeFromRooms()/setRooms() methods ) he
>> might be disappointed, as those methods would not run, but the
>> relationship will change...
>
> I'm wrestling with this issue myself: I've extended the basic
> templates so that events are fired on setter calls, but this practice
> has the exact shortcomings you pointed out.
> Is there a very good reason why cayenne objects don't fire events on a
> lower level (circumventing this problem) out of the box?
> Alternatively, if I expand my object code generation templates further
> so that objectA.removeFrom(objectB) fires a property change event for
> it's objectA.getBArray() as well as objectB.getToA() - will this
> completely solve the problem?
>
> t.n.a.

Craig Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:Craig.Russell@sun.com
P.S. A good JDO? O, Gasp!