Hi all, especially you, Chuck.

<preamble>
So, I've been working on this project that has been around since the 
pre-Practical WebObjects days and it has several places where EOs (entire 
graphs of EOs, really) are being manually copied. Entity by entity, 
attribute-by-attribute, relationship-by-relationship. The code is relatively 
easy to understand and has worked quite well for years, but it's a lot of code 
and needs to be regularly updated to handle new model attributes and such.

Then I came along.

"Ooooh!" I said. 

"This is soooo pre-2004! We should use EOCopyable," I said.  

"It would make our code much simpler and less prone to break," I said.  

"It's a lot fewer lines of code that we have to maintain because we won't have 
written it, Chuck did, years ago," I said.  

"We don't have to worry about _his_ code not working!" I said.

I'm very clever this way.

Only EOCopyable bitch-slapped me back to reality by refusing to work correctly 
right out of the gate, and I couldn't very well claim it was Chuck's fault, 
because, well, this is WebObjects. _I_ make things not work. Not Chuck. 

So here I've been all weekend (it's 4 in the morning, Monday morning now) and I 
think I've finally fixed it!

But as for the _why_ EOCopyable wasn't working … that I'm not quite sure of. 
EOF is a deep and murky ocean into which I wade with trepidation.
</preamble>

I ran into problems using the EOCopyable interface and classes described in 
Practical WebObjects. When I try to deepCopy an EO, which then deepCopies the 
EO's toMany relationship, I will end up with an extra related object (the 
original related object, plus 2 copies) One of the copies retains links to the 
original EO and the other is linked to the duplicate EO.

When EOF tries to insert both, it trips a unique constraint because the 
original EO now has two identical related objects (identical except for their 
PK)

I have the following structure:

ProgramYear <->> CountryForProgramYear <<-> Country

each Entity has numerous attributes and other relationships as well.

What I want to do is to create a new ProgramYear for 2013, only it's pretty 
much exactly the same as the 2012 ProgramYear, including being related to the 
same countries and the attributes for each related country are the same as 
well. EOCopyable to the rescue!

In EOCopyable terms, I did the following:

1) I made ProgramYear implement EOCopyable
2) the ProgramYear#duplicate(NSMutableDictionary) method on ProgramYear calls 
the EOCopyable.DefaultImplimentation#duplicate(NSMutableDictionary, 
EOEnterpriseObject)
3) I made CountryForProgramYear implement EOCopyable
4) the CountryForProgramYear#duplicate(NSMutableDictionary)  calls 
EOCopyable.Utility#shallowCopy(EOEnterpriseObject)

The problem is that I get constraint violations from the database because EOF 
is trying to insert two CountryForProgramYear records for one Country. One 
record for the new ProgramYear (the copy) and one for the _original_ 
ProgramYear.

"INSERT INTO "NSLIY"."COUNTRY_FOR_PROGRAM_YEAR"("ID", "SOME_FLAG_ID", 
"COUNTRY_ID", "PROGRAM_YEAR_ID", "DISPLAY_NAME") VALUES (1000032, 2, 1000001, 
4002057, 'USA')"
"INSERT INTO "NSLIY"."COUNTRY_FOR_PROGRAM_YEAR"("ID", "SOME_FLAG_ID", 
"COUNTRY_ID", "PROGRAM_YEAR_ID", "DISPLAY_NAME") VALUES (1000453, 2, 1000001, 
4002011, 'USA')"

Why is it trying to insert a CountryForProgram record that is associated with 
the original ProgramYear (4002011)?! CountryForProgram has a unique constraint 
on the combination of COUNTRY_ID and PROGRAM_YEAR_ID and this insert is 
violating it because COUNTRY_FOR_PROGRAM_YEAR already has a record where  
COUNTRY_ID = 1000001 and PROGRAM_YEAR_ID = 4002011 - the original that I 
requested EOCopyable copy!

As I stepped though the debug, I noticed something interesting. In 
EOCopyable.java immediately after this call (line 679 & 680):

EOEnterpriseObject originalCopy = ((EOCopyable)original).copy(copiedObjects);

The duplicate CountryForProgram is associated with original ProgramYear - which 
is how it should be because this is an exact copy. But when the next line is 
called, 

     if ( ! destinationObjects.containsObject(originalCopy))
     {
         destination.addObjectToBothSidesOfRelationshipWithKey(originalCopy, 
relationshipName);
     }

which adds the duplicated object to the destination's programYear relationship, 
change the relationship from pointing to the original object to pointing to the 
new ProgramYear object (destination), which it does, BUT (and here's the 
interesting part) the original ProgramYear#countriesForProgramYear() 
relationship still has two objects! One to the original CountryForProgram 
object AND one for the duplicate. When EOF tries to write both to the DB, it 
fails.

By simply changing the method to come at it from the other direction, 

         originalCopy.addObjectToBothSidesOfRelationshipWithKey(destination, 
relationship.inverseRelationship().name());

everything works as expected.

Here's the details:

WO 5.4.3, WOnder, Head of http://github.com/amagavi/wonder

ProgramYear.countryForPrograms relationship: 
        {
            deleteRule = EODeleteRuleCascade; 
            destination = CountryForProgramYear; 
            isToMany = Y; 
            joinSemantic = EOInnerJoin; 
            joins = ({destinationAttribute = programYearID; sourceAttribute = 
ID; }); 
            name = partnerOrgsForPIPY; 
            ownsDestination = Y; 
        }, 

CountryForProgram: 
    attributesUsedForLocking = (id); 
    className = "com.amagavi.ac.model.CountryForProgramYear"; 
    classProperties = (displayName); 
    externalName = "MULTI.COUNTR_FOR_PROGRAM_YEAR"; 
    fetchSpecificationDictionary = {}; 
    name = CountryForProgramYear; 
    primaryKeyAttributes = (id); 


CountryForProgramYear.programYear:
        {
            destination = ProgramYear; 
            isMandatory = Y; 
            isToMany = N; 
            joinSemantic = EOInnerJoin; 
            joins = ({destinationAttribute = ID; sourceAttribute = 
programYearID; }); 
            name = programYear; 
        }

I'm seeing this in many, but not all, to-many relationships and the kicker is 
that I see this exact same behavior on structures where the destination Entity 
of the toMany relationship has a compound PK.

It seems EOCopyable's calling of addObjectToBothSdesOfRelationshipWithKey 
doesn't actually work. But it should, right? 

I swear I've used this before with MSSQL Server and Oracle without any problem. 
Now I'm using FrontBase and it's breaking, but I don't see how the DB engine / 
plugin would have any impact on what I'm seeing. It seems almost like it isn't 
updating both sides of the relationship and leaving the toMany in a bad state.


Dave


—————————————————————————————
WebObjects - so easy that even Dave Avendasora can do it!™
—————————————————————————————
David Avendasora
Senior Software Abuser
Kaiten, Inc.




 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list      ([email protected])
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to [email protected]

Reply via email to