1

我正在尝试实现基于 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 类的对象实际上是树的核心,它们之间具有一对多的关系。Player 与 Rating 对象具有一对多的关系(假设游戏类型很少)。这是一个可以解决的直接问题:Range 和 Rating 对象之间有一对多的关系会很好,但 GAE 不支持多重父级。因此,Ranges 通过在 rating 对象中保存 parent range 键与 Ratings 具有手动管理的关系。出于同样的原因,Tree 无法管理与 Range 的一对一关系。它还使用存储在树对象中的根节点键手动处理。真正的问题发生在将第一个 Rating 插入树时,

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

我究竟做错了什么?

4

0 回答 0