Here's some code that reproduces my problem.  Before the code, I think
it's interesting to note that I started making this example in JPA
(because I had an old test that used JPA) and I was not able to
reproduce the error.  However, after converting to JDO, I get the
error.  I'm not 100% sure the code is exactly the same, however, but
wanted to note it in case it's helpful to anyone.

LocalServiceTestCase is based on the examples from Google.

Does anyone see a problem with the code or is this a legit bug?

@PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable = "true")
public class ParentOfOne {

        @PrimaryKey
        @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
        private Long id;

        @Persistent
        private SharedChild child;
        ...
}

@PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable = "true")
public class ParentOfList {

        @PrimaryKey
        @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
        private Long id;

        @Persistent
        private List<SharedChild> children;
        ...
}

@PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable = "true")
public class SharedChild {

        @PrimaryKey
        @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
        private Key id;

        @Persistent
        private String name;
        ...
}

public class SharedChildTestCase extends LocalServiceTestCase {

        private PersistenceManager persistenceManager;

        @Before
        public void openPersistenceManager() {
                PersistenceManagerFactory persistenceManagerFactory = JDOHelper
                                
.getPersistenceManagerFactory("transactions-optional");
                persistenceManager = 
persistenceManagerFactory.getPersistenceManager
();
        }

        @After
        public void closePersitenceManager() {
                persistenceManager.close();
        }

        @Test
        public void saveParents() {
                ParentOfList pol = new ParentOfList();
                List<SharedChild> list = new ArrayList<SharedChild>();
                SharedChild c1 = new SharedChild();
                c1.setName("c1");
                list.add(c1);
                SharedChild c2 = new SharedChild();
                c2.setName("c2");
                list.add(c2);
                pol.setChildren(list);
                persistenceManager.makePersistent(pol);

                ParentOfOne poo = new ParentOfOne();
                SharedChild c3 = new SharedChild();
                c1.setName("c3");
                poo.setChild(c3);
                persistenceManager.makePersistent(poo);
        }
}

On Sep 22, 9:40 am, objectuser <kevin.k.le...@gmail.com> wrote:
> Thanks, Marton.  That's very unfortunate.  The limitations still
> surprise me.  Is that documented somewhere?  Need to go back and
> reread that stuff I guess.
>
> I'll see if I can come up with a simple test case for the collection.
>
> On Sep 22, 3:28 am, Marton Papp <mapr...@gmail.com> wrote:
>
> > A class cannot have two different owned relationship to a single
> > class. For example if I have these two classes:
>
> > @PersistenceCapable(identityType = IdentityType.APPLICATION)
> > public class Parent3 {
>
> >         @PrimaryKey
> >         @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
> >         private Long id;
>
> >         Child1 child1;
>
> >         Child1 child2;
>
> > // ... get-set
>
> > }
>
> > @PersistenceCapable(identityType = IdentityType.APPLICATION)
> > public class Child1 {
>
> >     @PrimaryKey
> >     @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
> >     private Key id;
>
> > // ... get-set
>
> > }
>
> > then this code will fail with an assertion error:
>
> >         public void testMultiParent2() throws Exception {
>
> >                 PersistenceManager pm;
>
> >                 Parent3 p3 = null;
> >                 Parent3 p3Loaded = null;
>
> >                 pm = pmf.getPersistenceManager();
> >                 pm.currentTransaction().begin();
>
> >                 p3 = new Parent3();
> >                 p3.setChild1(new Child1());
> >                 p3.setChild2(new Child1());
> >                 pm.makePersistent(p3);
> >                 pm.currentTransaction().commit();
> >                 pm.close();
>
> >                 pm = pmf.getPersistenceManager();
> >                 pm.currentTransaction().begin();
>
> >                 p3Loaded = pm.getObjectById(Parent3.class, p3.getId());
>
> >                 Assert.assertNotSame(p3Loaded.getChild1(), 
> > p3Loaded.getChild2());
>
> >                 pm.currentTransaction().commit();
> >                 pm.close();
>
> >         }
>
> > It is because the datastore represents the relationship between Child1
> > and Parent3 instances by making the Parent3 instance the parent of the
> > Child1 instance. And it does the same for both Parent3.child1 and
> > Parent3.child2 relationships, so there is no way to tell which
> > property a particular instance of Child1 has been assigned to
> > originally.
>
> > I could not reproduce your problem with the list of children. Could
> > you send some code that fails for you?
>
> > Marton
>
> > On Sep 22, 3:31 am, objectuser <kevin.k.le...@gmail.com> wrote:
>
> > > After some more testing this is what I observe:
> > > - If A has one C property and B has two C properties, I can save both
> > > the A and the B instances, when I get the B instance, both C
> > > properties point to the same instance of C.
> > > - If A has one C property and B has a property that is a List of Cs, I
> > > get the exception in my original post.
>
> > > I guess I need to work this down to a repeatable example and file a
> > > defect report.
>
> > > On Sep 21, 2:49 pm, objectuser <kevin.k.le...@gmail.com> wrote:
>
> > > > I'll let leszek talk more about the code, but I assumed that the
> > > > commented out line was just testing both scenarios.  In the scenario
> > > > without comment on the line, it would be my scenario: the same "owned
> > > > type" but not the same "owned instance".
>
> > > > My original question is not about one particular entity having two
> > > > parents (in an owned relationship), but having two entity groups share
> > > > the same "owned" Java type.
>
> > > > Does that make sense?
>
> > > > On Sep 21, 2:00 pm, Marton Papp <mapr...@gmail.com> wrote:
>
> > > > > Hi,
>
> > > > > The code in that form also works for me, but just because the
> > > > > exception is caught and never reported. If you rethrow any exceptions
> > > > > from the catch blocks than you should get something like the
> > > > > following:
>
> > > > > Detected attempt to establish Parent2(3) as the parent of Parent1(1)/
> > > > > Child1(2) but the entity identified by Parent1(1)/Child1(2) is already
> > > > > a child of Parent1(1).  A parent cannot be established or changed once
> > > > > an object has been persisted.
> > > > > org.datanucleus.exceptions.NucleusUserException: Detected attempt to
> > > > > establish Parent2(3) as the parent of Parent1(1)/Child1(2) but the
> > > > > entity identified by Parent1(1)/Child1(2) is already a child of 
> > > > > Parent1
> > > > > (1).  A parent cannot be established or changed once an object has
> > > > > been persisted.
> > > > >         at
> > > > > org.datanucleus.store.appengine.DatastoreRelationFieldManager.checkForParentSwitch
> > > > > (DatastoreRelationFieldManager.java:214)
> > > > >         at 
> > > > > org.datanucleus.store.appengine.DatastoreRelationFieldManager
> > > > > $1.setObjectViaMapping(DatastoreRelationFieldManager.java:129)
> > > > >         at 
> > > > > org.datanucleus.store.appengine.DatastoreRelationFieldManager
> > > > > $1.apply(DatastoreRelationFieldManager.java:108)
> > > > >         at
> > > > > org.datanucleus.store.appengine.DatastoreRelationFieldManager.storeRelations
> > > > > (DatastoreRelationFieldManager.java:80)
> > > > >         at
> > > > > org.datanucleus.store.appengine.DatastoreFieldManager.storeRelations
> > > > > (DatastoreFieldManager.java:795)
> > > > >         at
> > > > > org.datanucleus.store.appengine.DatastorePersistenceHandler.insertPostProcess
> > > > > (DatastorePersistenceHandler.java:288)
> > > > >         at
> > > > > org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObjects
> > > > > (DatastorePersistenceHandler.java:241)
> > > > >         at
> > > > > org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObject
> > > > > (DatastorePersistenceHandler.java:225)
> > > > >         at 
> > > > > org.datanucleus.state.JDOStateManagerImpl.internalMakePersistent
> > > > > (JDOStateManagerImpl.java:3185)
> > > > >         at org.datanucleus.state.JDOStateManagerImpl.makePersistent
> > > > > (JDOStateManagerImpl.java:3161)
> > > > >         at org.datanucleus.ObjectManagerImpl.persistObjectInternal
> > > > > (ObjectManagerImpl.java:1298)
> > > > >         at org.datanucleus.ObjectManagerImpl.persistObject
> > > > > (ObjectManagerImpl.java:1175)
> > > > >         at org.datanucleus.jdo.JDOPersistenceManager.jdoMakePersistent
> > > > > (JDOPersistenceManager.java:669)
> > > > >         at org.datanucleus.jdo.JDOPersistenceManager.makePersistent
> > > > > (JDOPersistenceManager.java:694)
> > > > >         at 
> > > > > hu.mapro.gae.test.forum.TestForum.testMultiParent(TestForum.java:
> > > > > 44)
> > > > >         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> > > > >         at sun.reflect.NativeMethodAccessorImpl.invoke
> > > > > (NativeMethodAccessorImpl.java:39)
> > > > >         at sun.reflect.DelegatingMethodAccessorImpl.invoke
> > > > > (DelegatingMethodAccessorImpl.java:25)
> > > > >         at java.lang.reflect.Method.invoke(Method.java:597)
> > > > >         at 
> > > > > org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
> > > > >         at org.junit.internal.runners.MethodRoadie.runTestMethod
> > > > > (MethodRoadie.java:98)
> > > > >         at 
> > > > > org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:
> > > > > 79)
> > > > >         at
> > > > > org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters
> > > > > (MethodRoadie.java:87)
> > > > >         at 
> > > > > org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:
> > > > > 77)
> > > > >         at 
> > > > > org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
> > > > >         at 
> > > > > org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod
> > > > > (JUnit4ClassRunner.java:88)
> > > > >         at org.junit.internal.runners.JUnit4ClassRunner.runMethods
> > > > > (JUnit4ClassRunner.java:51)
> > > > >         at org.junit.internal.runners.JUnit4ClassRunner$1.run
> > > > > (JUnit4ClassRunner.java:44)
> > > > >         at org.junit.internal.runners.ClassRoadie.runUnprotected
> > > > > (ClassRoadie.java:27)
> > > > >         at org.junit.internal.runners.ClassRoadie.runProtected
> > > > > (ClassRoadie.java:37)
> > > > >         at org.junit.internal.runners.JUnit4ClassRunner.run
> > > > > (JUnit4ClassRunner.java:42)
> > > > >         at 
> > > > > org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run
> > > > > (JUnit4TestReference.java:46)
> > > > >         at org.eclipse.jdt.internal.junit.runner.TestExecution.run
> > > > > (TestExecution.java:38)
> > > > >         at 
> > > > > org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests
> > > > > (RemoteTestRunner.java:467)
> > > > >         at 
> > > > > org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests
> > > > > (RemoteTestRunner.java:683)
> > > > >         at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run
> > > > > (RemoteTestRunner.java:390)
> > > > >         at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main
> > > > > (RemoteTestRunner.java:197)
>
> > > > > The error message is self-explanatory. According to my knowledge it is
> > > > > impossible for a single entity to have multiple parents, just as it is
> > > > > impossible to change the parent of an entity (or any other part of its
> > > > > primary key).
>
> > > > > But you were talking about a "class" being in owned realationship with
> > > > > two different classes, not an "entity". If I remove the comment from
> > > > > the code above that creates a new child entity before assigning it to
> > > > > the second parent instance then it is working as expected. Of course
> > > > > they will be two different employee instances with different parents
> > > > > (of different types).
>
> > > > > Marton
>
> > > > > On Sep 21, 4:49 pm, leszek <leszek.ptokar...@gmail.com> wrote:
>
> > > > > > That's very interesting because it
>
> ...
>
> read more »
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Google App Engine for Java" group.
To post to this group, send email to google-appengine-java@googlegroups.com
To unsubscribe from this group, send email to 
google-appengine-java+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/google-appengine-java?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to