GAE数据存储区不会在嵌入式JDO类中保持拥有的1:1关系

时间:2013-04-30 06:00:09

标签: java google-app-engine jdo datanucleus object-persistence

我有三个@PC课程:

@PersistenceCapable
class A {
  @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
  private String                  id;

  @Persistent @Embedded
  private B b;

  public void setB(B b){
    this.b=b;
  }
}

@PersistenceCapable @EmbeddedOnly
class B {
  @Persistent
  private String someInfo;
  @Persistent
  private C c;

  public void setC(C c){
    this.c=c;
  }
}

@PersistenceCapable
class C {
  @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
  private String                  id;

  @Persistent
  private String value;

  public void setValue(String value){
    this.value=value;
  }
}

我想实现B持有与A相同的实体,同时持有对C的引用但GAE不允许我,我在提交时得到以下异常:

Detected attempt to establish A(1) as the parent of C(2) but the entity identified by C(2) has already been persisted without a parent.  A parent cannot be established or changed once an object has been persisted.

在此代码中:

A a = new A();
B b = new B();
C c = new C();
c.setValue("foo");
b.setC(c);
a.setB(b);

m.makePersistent(a);

另外:看看DatastoreViewer向我显示C已被持久化!但是A缺失了。这可能是因为我没有明确地回滚事务中的事务,这在这种情况下是不相关的,但是显示C是在其父A之前编写的。

我错过了什么? TX

更新2:

如建议我明确启用了交易:

Transaction tx = pm.currentTransaction();
try {
  tx.begin();
  pm.makePersistent(a);
  tx.commit();
} finally {
  if (tx.isActive()) {
    tx.rollback();
  }
  pm.close();
}

当执行.makePersistent()没有显式事务时抛出相同的异常。然后我在JDO config中设置了禁用全局交叉tx选项:

<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="false"/>

现在得到一个可能提示的不同异常:

cross-group transaction need to be explicitly specified, see 
TransactionOptions.Builder.withXGfound both Element {
  type: "A"
  id: 1
}
and Element {
  type: "C"
  id: 2
}

2 个答案:

答案 0 :(得分:0)

如果您为此设置了事务边界,我认为您的问题就会消失。虽然documentation声称您应该可以执行的操作,但我认为您已在datanucleus.appengine.autoCreateDatastoreTxns中将true设置为jdoconfig.xml。 因此,C与其自己的实体组一起存储在自己的事务中。在第二个交易中,您存储了A,因此尝试将C重新分配给A的被禁止的实体组:

  

因为实体组只能在创建实体时分配

所以:设置一个交易(按照推荐)。

答案 1 :(得分:0)

好的,我明白了,

然而我认为DataStore的JDO实现(我不知道它的DataNucleus作业是否会遗漏)。根据{{​​3}},通常只有祖先的实体可以一次性保留,特别是GAE 声明以支持跨组事务,这些事务受数量限制但可以保持不相关的实体路径。两者都不适用于我的情况。

穷人解决方案现在通过密钥(GAE专有)强制执行无主关系,这是描述祖先路径的唯一可能的kandidate,以便DataStore正确地跟随我的POJO的扩展:

class A {
  ...
  private String naturalPK;
  public String getNaturalPK(){
    return naturalPK;
  }
  ...
}

class C {
  ...
  public void setId(String id){
    this.id=id;
  }
  ...
}

和持久性代码:

  tx.begin();

  // we have to assign parent/child keys to enforce ancestor path
  Key parentKey = KeyFactory.createKey("A", A.getNaturalPK());
  a.setId(KeyFactory.keyToString(parentKey));

  // now build child key
  a.getB().getC().setId(KeyFactory.createKeyString(parentKey, "C", A.getNaturalPK()));

  pm.makePersistent(offerer);
  tx.commit(); // works.

一个问题是我不能在这里使用代理键,另一个问题是我不希望非JDO和非BO代码在我的POJO中,所以必须在JDO中的某个地方建立父子关系-DAO实施。我认为DataNucleus GAE端口在这里有些不准确,因为持久性图看起来是相反的:)