1

Range オブジェクトのツリー構造に基づいて構築されたプレイヤー ランキングを実装しようとしています。評価オブジェクトは、ツリーの最下位レベルにアタッチされています。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;
}

次に、ルート Range オブジェクトを保持するツリー自体 (Tree) を表すクラスがあります (ここには示されていません)。ご覧のように、Range クラスのオブジェクトは実際にはツリー コアであり、それらの間に 1 対多の関係があります。Player は、Rating オブジェクトと 1 対多の関係にあります (ゲームの種類が少ないと仮定します)。Range オブジェクトと Rating オブジェクトの間に 1 対多の関係があればよいのですが、GAE は複数の親子関係をサポートしていません。したがって、範囲は、評価オブジェクトで親範囲キーを保持することにより、評価と手動で管理された関係を持ちます。同じ理由で、Tree は Range との 1 対 1 の関係を管理できません。また、ツリー オブジェクトに格納されているルート ノード キーを使用して手動で処理されます。本当の問題は、最初の評価をツリーに挿入するときに発生します。

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