Gae Jdo 对具有双向导航的一对多拥有关系的持久性

Posted

技术标签:

【中文标题】Gae Jdo 对具有双向导航的一对多拥有关系的持久性【英文标题】:Gae Jdo persistance on one-to-many owned relationship with bidirectional navigation 【发布时间】:2010-12-30 13:44:26 【问题描述】:

我正在尝试使用 JDO 在 GAE 中与双向导航保持一对多的拥有关系。

我手动将Contact 添加到User 类,我希望最终Contact 将引用父User 对象。

如果我在持久化父对象之前手动配置它,我会得到一个 例外:org.datanucleus.store.appengine.DatastoreRelationFieldManager.checkForParentSwitch(DatastoreRelationFieldManager.java:204) User 对象持久化后,父引用不再 更新。 Contact 对象从 使用未更新父引用的密钥的数据存储区。

我不明白我的错误在哪里。

package test;

import java.util.ArrayList;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.google.appengine.api.datastore.Key;

public class DatastoreJdoTest extends LocalServiceTestCase 
    @Autowired
    @Qualifier("persistenceManagerFactory")
    PersistenceManagerFactory pmf;

    @Test
    public void testBatchInsert() 
        Key contactKey;
        PersistenceManager pm = pmf.getPersistenceManager();
        try 
            pm.currentTransaction().begin();
            User user = new User();
            Contact contact = new Contact("contact1");
            user.contacts.add(contact);

            /*
             * With this an exception is thrown
             * 
             * Detected attempt to establish User(1)/Contact(2) as the parent of
             * User(1) but the entity identified by User(1) has already been
             * persisted without a parent. A parent cannot be established or
             * changed once an object has been persisted.
             * org.datanucleus.store.appengine.FatalNucleusUserException:
             * Detected attempt to establish User(1)/Contact(2) as the parent of
             * User(1) but the entity identified by User(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.checkForParentSwitch
             * (DatastoreRelationFieldManager.java:204)
             */
            //contact.user = user;
            Assert.assertNull(contact.key);
            pm.makePersistent(user);
            Assert.assertNotNull(contact.key);

            pm.currentTransaction().commit();

            contactKey = contact.key;
            //this assertion is broken. why ? 
            //Assert.assertNotNull(contact.user);
         finally 
            if (pm.currentTransaction().isActive()) 
                pm.currentTransaction().rollback();
            
        
        Contact contact2 = pm.getObjectById(Contact.class, contactKey);
        Assert.assertNotNull(contact2);
        //this assertion is broken. why the contact don't store the parent user ?
        Assert.assertNotNull(contact2.user);
    


@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
class User 
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    public Key key;
    @Persistent
    public String name;
    @Persistent
    public List<Contact> contacts = new ArrayList<Contact>();


@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
class Contact 
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    Key key;
    @Persistent
    public String contact;
    @Persistent(mappedBy = "contacts", dependent = "true")
    public User user;

    public Contact(String contact) 
        this.contact = contact;
    

【问题讨论】:

【参考方案1】:

根据App Engine docs,您应该在关系的所有者中指定“mappedBy”。

您可能还想阅读Max Ross's article 或查看my code,它从查询中获取的子对象(消息)访问父对象(讨论)

【讨论】:

确实我忘了在 User.contacts 字段中添加 mappedBy @Persistent(mappedBy="user") public List<contact> contacts = new ArrayList<contact>(); </contact></contact>【参考方案2】:

只需发布带有 Dmitry 指出的更正的代码,以便于阅读:

import java.util.ArrayList;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.google.appengine.api.datastore.Key;

public class DatastoreJdoTest extends LocalServiceTestCase 
 @Autowired
 @Qualifier("persistenceManagerFactory")
 PersistenceManagerFactory pmf;

 @Test
 public void testBatchInsert() 
  Key contactKey;
  PersistenceManager pm = pmf.getPersistenceManager();
  try 
   pm.currentTransaction().begin();
   User user = new User();
   Contact contact = new Contact("contact1");
   user.contacts.add(contact);

   /*
    * With this an exception is thrown
    * 
    * Detected attempt to establish User(1)/Contact(2) as the parent of
    * User(1) but the entity identified by User(1) has already been
    * persisted without a parent. A parent cannot be established or
    * changed once an object has been persisted.
    * org.datanucleus.store.appengine.FatalNucleusUserException:
    * Detected attempt to establish User(1)/Contact(2) as the parent of
    * User(1) but the entity identified by User(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.checkForParentSwitch
    * (DatastoreRelationFieldManager.java:204)
    */
   //contact.user = user;
   Assert.assertNull(contact.key);
   pm.makePersistent(user);
   Assert.assertNotNull(contact.key);

   pm.currentTransaction().commit();

   contactKey = contact.key;
   //this assertion is broken. why ? 
   //Assert.assertNotNull(contact.user);
   finally 
   if (pm.currentTransaction().isActive()) 
    pm.currentTransaction().rollback();
   
  
  Contact contact2 = pm.getObjectById(Contact.class, contactKey);
  Assert.assertNotNull(contact2);
  //this assertion is broken. why the contact don't store the parent user ?
  Assert.assertNotNull(contact2.user);
 


@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
class User 
 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 public Key key;
 @Persistent
 public String name;
 @Persistent(mappedBy = "user", dependent = "true")
 public List<Contact> contacts = new ArrayList<Contact>();


@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
class Contact 
 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 Key key;
 @Persistent
 public String contact;
 @Persistent
 public User user;

 public Contact(String contact) 
  this.contact = contact;
 

【讨论】:

无评论权限的用户发表评论 (profile):dependent 用于属性,用于属性集合,使用dependeElement

以上是关于Gae Jdo 对具有双向导航的一对多拥有关系的持久性的主要内容,如果未能解决你的问题,请参考以下文章

06章 映射一对多双向关联关系以及cascadeinverse属性

06章 映射一对多双向关联关系以及cascadeinverse属性

在 GAE、Java、JDO 等环境中,DAO 是啥?

JPA 一对多双向堆栈溢出问题

Hibernate关联映射关系

JPA:如何具有相同实体类型的一对多关系