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属性