Google AppEngine GAE排名树实现事务问题

时间:2012-11-29 23:00:20

标签: java google-app-engine transactions google-cloud-datastore

我正在尝试实现基于Range对象的树结构构建的玩家排名,并在树的最低层附加了Rating对象。我知道有一个Java实现(从Python移植),但它使用低级数据存储API,我想使用JPA。以下是描述结构的代码段:

@Entity
public class Range
{
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Key key;
  @ManyToOne(optional = false)
  private Range parent;
  // leaf node?
  private boolean leaf;
  // total number of rating objects in the node's subtree
  private long count;
  // child nodes
  @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  private Set<Range> children;
  // range [max, min]
  private long max;
  private long min;

  @Version
  private int version;
}

@Entity
public class Rating
{
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Key key;
  // parent range
  private Key range;
  @ManyToOne(optional = false)
  private Player player;
  // rating value
  private long value;

  @Version
  private int version;
}

@Entity
public class Player
{
  @Id
  @GeneratedValue(strategy= GenerationType.IDENTITY)
  private Key key;
  // list of associated ratings for different game configurations (one per config)
  @OneToMany(mappedBy = "profile", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  private Collection<Rating> ratings = new LinkedList<Rating>();

  @Version
  private int version;
}

然后有一个表示树本身的类(Tree),它保存根Range对象(此处未显示)。 正如您所看到的,Range类的对象实际上是树核,它们之间具有一对多的关系。玩家与评级对象有一对多的关系(假设游戏类型很少)。 这是一个直接的问题,可以解决:在Range和Rating对象之间建立一对多关系会很好,但是GAE不支持多个父级。因此,Ranges通过在评级对象中保留父范围键,与评级具有手动管理关系。出于同样的原因,Tree无法与Range建立一对一的关系。它也可以使用存储在树对象中的根节点键手动处理。 将第一个评级插入树时会发生真正的问题,直到现在才存在:

EntityTransaction tx = em.getTransaction();

// start transaction
tx.begin();

// create the tree
Tree tree = new Tree();
em.persist(tree);
// and its root range
Range root = new Range();
em.persist(root);
// we need range key to attach it to the tree
em.flush();
// attach root range to the tree
tree.setRoot(root);
// save the tree
em.persist(tree);
em.flush();

// ...
// we have parent range (tree root), player and score
Rating rating = new Rating(); 
// one-to-many relationship: Player--<>Rating
player.getRatings().add(rating);
rating.setPlayer(player);
// attach to the parent range
rating.setParent(range);
rating.setValue(score);
// use entity manager to persist the new rating
em.persist(rating);

tx.commit();  <------------ here it fails

以下是缩短的异常转储:

javax.persistence.RollbackException: Transaction failed to commit
  at org.datanucleus.jpa.EntityTransactionImpl.commit(EntityTransactionImpl.java:118)
  at org.datanucleus.store.appengine.jpa.DatastoreEntityTransactionImpl.commit(DatastoreEntityTransactionImpl.java:58)
  ...
Caused by: javax.persistence.OptimisticLockException: Some instances failed to flush successfully due to optimistic verification problems.
  at org.datanucleus.jpa.NucleusJPAHelper.getJPAExceptionForNucleusException(NucleusJPAHelper.java:271)
  at org.datanucleus.jpa.EntityTransactionImpl.commit(EntityTransactionImpl.java:116)
  ... 39 more
Caused by: org.datanucleus.exceptions.NucleusOptimisticException: Optimistic concurrency exception updating ferp.center.server.entity.Range with pk Range(10).  The underlying entity had already been deleted.
  at org.datanucleus.store.appengine.DatastorePersistenceHandler.newNucleusOptimisticException(DatastorePersistenceHandler.java:391)
  at org.datanucleus.store.appengine.DatastorePersistenceHandler.handleVersioningBeforeWrite(DatastorePersistenceHandler.java:412)
  at org.datanucleus.store.appengine.DatastorePersistenceHandler.updateObject(DatastorePersistenceHandler.java:574)
  at org.datanucleus.state.JDOStateManagerImpl.flush(JDOStateManagerImpl.java:4576)
  at org.datanucleus.ObjectManagerImpl.flushInternal(ObjectManagerImpl.java:2814)
  at org.datanucleus.ObjectManagerImpl.flush(ObjectManagerImpl.java:2754)
  at org.datanucleus.ObjectManagerImpl.preCommit(ObjectManagerImpl.java:2893)
  at org.datanucleus.TransactionImpl.internalPreCommit(TransactionImpl.java:369)
  at org.datanucleus.TransactionImpl.commit(TransactionImpl.java:256)
  at org.datanucleus.jpa.EntityTransactionImpl.commit(EntityTransactionImpl.java:104)
  ... 39 more

我做错了什么?

0 个答案:

没有答案