On 2012-03-25, at 2:16 PM, David Avendasora wrote:

> Hi all, especially you, Chuck.

Oh Gawd!  It is that Avendasora guy.  Again!


> <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.

I am certain that EOF feels the same way. :-P


> </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)

As soon as I hear "extra object" I immediately think "Owns Destination and 
Propagate Primary Key" as EOF likes to helpfully auto create objects.  But one 
of them is not empty with no attribute values, is it?


> 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)?!

It looks like a shallow copy of ProgramYear.


> 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

Are you sure that this is only creating one CountryForProgramYear?  My guess is 
that it is creating one and EOF is creating a second.  Somehow.


> - 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.

Though that suggests that something else is wrong unless it is 
addObjectToBothSidesOfRelationshipWithKey that is somehow creating the second 
new object.  Trying breaking in the CountryForProgramYear and see where each is 
created from.



> 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

Are they modeled exactly the same?


> 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? 

It should, but your EO or superclasses maybe subtly altering what it does.


> 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.

I still think it sounds like something in your model that is causing the 
copying to not work like you intend.  I am not saying the model is wrong, but 
you maybe have something setup that I never tested with.


Chuck


-- 
Chuck Hill             Senior Consultant / VP Development

Practical WebObjects - for developers who want to increase their overall 
knowledge of WebObjects or who are trying to solve specific problems.    
http://www.global-village.net/gvc/practical_webobjects








Attachment: smime.p7s
Description: S/MIME cryptographic signature

 _______________________________________________
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