MountainClimb / datanucleus-appengine

Automatically exported from code.google.com/p/datanucleus-appengine
0 stars 0 forks source link

Support interfaces for defining BIDIRECTIONAL 1:1 and 1:m relationships in JDO #207

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
Copied from http://code.google.com/p/googleappengine/issues/detail?id=1732

When creating a one-to-many relationship using interfaces (in the parent
class), persistence fails

Source files attached. I test classes use TestNG, you would want to change
that to something you are familiar with.

Main parts of the code are:
(The exception is shown at the bottom.)

/*************************************************************/
public interface ParentInterface {
//This interface does not have to be empty
}

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class ParentImpl implements ParentInterface{
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName = "datanucleus", key = "gae-encoded.pk", value = "true")
    private Key id;

    @Persistent(mappedBy="parent") 
    @Extension(vendorName = "datanucleus", key = "implementation-classes",
value = "bug.ChildImpl")
    @Order(extensions ={@Extension(vendorName="datanucleus",
key="list-ordering", value = "id ASC")} )
    private List<ChildInterface> children = new ArrayList<ChildInterface>();
    /**
     * Replacing the "children" attribute with the following will not break
the test
    private List<ChildImpl> children = new ArrayList<ChildImpl>();
    **/

    public ParentImpl(){}
    public void addChild(ChildInterface child){
        children.add((ChildImpl)child);
    }

    public Key getId(){
        return this.id;
    }

}

/*************************************************************/
public interface ChildInterface {
    public ParentInterface getParent();
}
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class ChildImpl implements ChildInterface {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName = "datanucleus", key = "gae-encoded.pk", value = "true")
    private Key id;

    @Persistent
    private ParentInterface parent;

    public ChildImpl(ParentInterface parent){
        this.parent = parent;
    }

    @Override
    public ParentInterface getParent() {
        return this.parent;
    }

    public Key getId(){
        return this.id;
    }

}

/*************************************************************/

public class OneToManyBugTest extends AbstractTestNG{
    public static final Logger log =
Logger.getLogger(OneToManyBugTest.class.getSimpleName());

    @Test(suiteName="gaebug", groups = "persistence")
    public void createContent(){
        ParentImpl parent = new ParentImpl();
        PersistenceUtil.persist(parent); //uses transactional persist
        Assert.assertNotNull(parent.getId());
        System.out.println("Parent is persisted with ID: " + parent.getId());
        ChildImpl child = new ChildImpl(parent);
        parent.addChild(child);
        PersistenceUtil.persist(parent); //uses transcational persist
        Assert.assertNotNull(child.getId());
    }
}

/*************************************************************/

    public static void persist(Object o){
        System.out.println("Making persistent using: " + pm.get());
        Transaction tx = pm.get().currentTransaction();
        try{
            tx.begin();
            pm.get().makePersistent(o);
            tx.commit();
        }finally{
            if ( tx.isActive())tx.rollback();
        }
        System.out.println("Finished Making persistent using: " + pm.get());
    }
/*************************************************************/

Exception:

Detected attempt to establish ChildImpl(2) as the parent of ParentImpl(1)
but the entity identified by ParentImpl(1) has already been persisted
without a parent.  A parent cannot be established or changed once an object
has been persisted.
org.datanucleus.exceptions.NucleusUserException: Detected attempt to
establish ChildImpl(2) as the parent of ParentImpl(1) but the entity
identified by ParentImpl(1) has already been persisted without a parent.  A
parent cannot be established or changed once an object has been persisted.
    at
org.datanucleus.store.appengine.DatastoreRelationFieldManager.checkForParentSwit
ch(Datastore
RelationFieldManager.java:206)
    at
org.datanucleus.store.appengine.DatastoreRelationFieldManager$1.setObjectViaMapp
ing(Datasto
reRelationFieldManager.java:129)
    at
org.datanucleus.store.appengine.DatastoreRelationFieldManager$1.apply(DatastoreR
elationFieldM
anager.java:108)
    at
org.datanucleus.store.appengine.DatastoreRelationFieldManager.storeRelations(Dat
astoreRelation
FieldManager.java:80)
    at
org.datanucleus.store.appengine.DatastoreFieldManager.storeRelations(DatastoreFi
eldManager.ja
va:770)
    at
org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObject(Datasto
rePersistenceH
andler.java:231)
    at
org.datanucleus.state.JDOStateManagerImpl.internalMakePersistent(JDOStateManager
Impl.java:30
67)
    at
org.datanucleus.state.JDOStateManagerImpl.makePersistent(JDOStateManagerImpl.jav
a:3043)
    at
org.datanucleus.ObjectManagerImpl.persistObjectInternal(ObjectManagerImpl.java:1
258)
    at org.datanucleus.sco.SCOUtils.validateObjectForWriting(SCOUtils.java:1365)
    at
org.datanucleus.store.mapped.scostore.ElementContainerStore.validateElementForWr
iting(Elemen
tContainerStore.java:401)
    at
org.datanucleus.store.mapped.scostore.FKListStore.validateElementForWriting(FKLi
stStore.java:76
4)
    at
org.datanucleus.store.mapped.scostore.FKListStore.internalAdd(FKListStore.java:5
03)
    at
org.datanucleus.store.mapped.scostore.AbstractListStore.add(AbstractListStore.ja
va:123)
    at org.datanucleus.sco.backed.List.add(List.java:752)
    at bug.ParentImpl.addChild(ParentImpl.java:34)
    at bug.OneToManyBugTest.createContent(OneToManyBugTest.java:18)
... Removed 22 stack frames

Original issue reported on code.google.com by jasonaco...@google.com on 26 May 2010 at 7:39

GoogleCodeExporter commented 8 years ago
I just tested this using 1.3.3, and I see a different exception although it 
still fails to persist. My code doesn't 
even get past the point of persisting the parent. Because the Parent JDO class 
references interfaces instead of 
implementations, I see the following:

Problem accessing /_732test2. Reason:

    Error creating the MetaDataManager for API "JDO" : 
Caused by:

javax.jdo.JDOException: Error creating the MetaDataManager for API "JDO" : 
    at 
org.datanucleus.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJ
DOHelper.java:419)
    at org.datanucleus.jdo.JDOPersistenceManager.jdoMakePersistent(JDOPersistenceManager.java:674)
    at org.datanucleus.jdo.JDOPersistenceManager.makePersistent(JDOPersistenceManager.java:694)
    at com.google.appengine.demo.PersistenceUtil.persist(PersistenceUtil.java:12)
    at com.google.appengine.demo._732test2Servlet.doGet(_732test2Servlet.java:13)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:693)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:51)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:122)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at 
com.google.apphosting.utils.jetty.DevAppEngineWebAppContext.handle(DevAppEngineW
ebAppContext.java:7
0)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at 
com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.han
dle(JettyContainerService
.java:349)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
NestedThrowablesStackTrace:
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at 
sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorA
ccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:501)
    at com.google.appengine.tools.development.agent.runtime.Runtime.newInstance_(Runtime.java:112)
    at com.google.appengine.tools.development.agent.runtime.Runtime.newInstance(Runtime.java:120)
    at 
org.datanucleus.plugin.NonManagedPluginRegistry.createExecutableExtension(NonMan
agedPluginRegistry.java
:572)
    at 
org.datanucleus.store.appengine.DatastorePluginRegistry.createExecutableExtensio
n(DatastorePluginRegistry.j
ava:124)
    at org.datanucleus.plugin.PluginManager.createExecutableExtension(PluginManager.java:324)
    at org.datanucleus.OMFContext.getMetaDataManager(OMFContext.java:469)
    at org.datanucleus.ObjectManagerImpl.getMetaDataManager(ObjectManagerImpl.java:407)
    at org.datanucleus.ObjectManagerImpl.hasPersistenceInformationForClass(ObjectManagerImpl.java:3974)
    at org.datanucleus.ObjectManagerImpl.assertClassPersistable(ObjectManagerImpl.java:3892)
    at org.datanucleus.ObjectManagerImpl.persistObjectInternal(ObjectManagerImpl.java:1240)
    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 com.google.appengine.demo.PersistenceUtil.persist(PersistenceUtil.java:12)
    at com.google.appengine.demo._732test2Servlet.doGet(_732test2Servlet.java:13)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:693)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:51)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:122)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at 
com.google.apphosting.utils.jetty.DevAppEngineWebAppContext.handle(DevAppEngineW
ebAppContext.java:7
0)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at 
com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.han
dle(JettyContainerService
.java:349)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: java.lang.NullPointerException
    at 
org.datanucleus.store.appengine.jdo.DatastoreJDOMetaDataManager.shouldBeInDefaul
tFetchGroup(DatastoreJ
DOMetaDataManager.java:167)
    at 
org.datanucleus.store.appengine.jdo.DatastoreJDOMetaDataManager.shouldBeInDefaul
tFetchGroup(DatastoreJ
DOMetaDataManager.java:167)
    at 
org.datanucleus.store.appengine.jdo.DatastoreJDOMetaDataManager.populateAbstract
ClassMetaData(Datastor
eJDOMetaDataManager.java:153)
    at org.datanucleus.metadata.MetaDataManager.populateFileMetaData(MetaDataManager.java:2148)
    at org.datanucleus.metadata.MetaDataManager.loadAnnotationsForClass(MetaDataManager.java:2289)
    at 
org.datanucleus.jdo.metadata.JDOMetaDataManager.getMetaDataForClassInternal(JDOM
etaDataManager.java:3
69)
    at 
org.datanucleus.jdo.metadata.JDOMetaDataManager$MetaDataRegisterClassListener.re
gisterClass(JDOMetaDat
aManager.java:184)
    at javax.jdo.spi.JDOImplHelper.addRegisterClassListener(JDOImplHelper.java:462)
    at org.datanucleus.jdo.metadata.JDOMetaDataManager.<init>(JDOMetaDataManager.java:171)
    at 
org.datanucleus.store.appengine.jdo.DatastoreJDOMetaDataManager.<init>(Datastore
JDOMetaDataManager.ja
va:136)
    ... 46 more

Original comment by jasonaco...@google.com on 26 May 2010 at 7:48

Attachments:

GoogleCodeExporter commented 8 years ago
Bidirectional one-to-many mappings are failing for me in JPA as well.

Original comment by ganesh...@gmail.com on 1 Sep 2010 at 5:41

GoogleCodeExporter commented 8 years ago
really annoying!

Original comment by software...@gmail.com on 28 Nov 2010 at 9:27

GoogleCodeExporter commented 8 years ago
Same here. Had to change ArrayList<Interface> to ArrayList<Implementation>

Original comment by tadas.subonis@gmail.com on 2 Jan 2011 at 12:22

GoogleCodeExporter commented 8 years ago
The issue seems to be isolated to bidirectional interface relations, something 
that DataNucleus core still needs work on. Unidirectional interface relations 
ought to work perfectly well, and if not then provide a testcase

Original comment by googleco...@yahoo.co.uk on 31 Oct 2011 at 7:47

GoogleCodeExporter commented 8 years ago
SVN trunk, when used with DataNucleus 3.0.3 (SNAPSHOT), works on the provided 
case now.

Original comment by googleco...@yahoo.co.uk on 1 Nov 2011 at 10:20