0

私はHibernateでEJB 3を使用しています。ステートレス セッション Bean があります。その Bean にはメソッド deleteItem があります。

クライアントが deleteItem メソッドを呼び出すと、問題なく削除が行われました。しかし、for ループを使用して deleteItem メソッドを呼び出し、そのループの制限を 5 ~ 10 回設定しようとすると、削除が失敗することがあります。しかしいつもではない。

削除操作は、実際には 2 つのテーブルからデータを削除します。子テーブルと親テーブル。各削除は、フラッシュ操作を実行することによってコミットされます。

すでに述べたように、削除を1つずつ実行しても問題は発生せず、同時に実行しようとした場合にのみ発生します。私が得ている例外は以下のとおりです

Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key
constraint fails (`functionTest/MotherTable`, CONSTRAINT `FKBC6CB0E6D5545EFD` FOREIGN KEY
(`MotherTable_FieldId`) REFERENCES `ChildTable` (`childTableId`))

ここで同時削除操作を行う方法はありません。また、アイテムの削除は、他のアイテムの削除操作とは関係ありません。そのため、同時実行が発生しても、複数のアイテムを同時に削除しても問題ありません。

そのため、「クライアントが複数のスレッドで同じ Bean インスタンスにアクセスしている可能性があります」という決定に至りました。このような状況では、2 つのスレッドが同じ Entity Manager State を異なる状態に保ちます。一方は永続コンテキストのフラッシュを試みますが、もう一方は子アイテムの削除をまだ完了していません。その時点で、BatchUpdateException が発生しました。- それは私の観察です。私はそれについて100%確信が持てません。

この状況を克服するために、私は楽観的ロックを採用しました。マザーテーブルにバージョン列を作成しました。今、私は OptimisticLockException を取得しています。しかし、例外をキャッチできません。以下は、OptimisticLockException をキャッチするために使用しているコードです。

private boolean deleteItem(Item itemId) {

           Item item= getItem(itemId);
       removeChildTableData(item);
       mEm.remove(item);

    try
            {
       mEm.flush();
    }
    catch (OptimisticLockException  e) 
            {
         try {
            Thread.sleep(1000);
             } 
                     catch (InterruptedException e1) {
            e1.printStackTrace();
         }
       deleteItem(itemId);

        }
    catch(Exception ex)
                 {

        if (ex.getCause() instanceof OptimisticLockException) 
                     {
                       try {
                            Thread.sleep(1000);
                           } catch (InterruptedException x) {
                           }
                     deleteItem(itemId);

                     }


      }

    return true;
   }

したがって、私の目標は OptimisticLockException をキャッチし、削除操作を再実行することです。例外クラス名を確認しましたが、EntityNotFound です。しかし、スタック トレースで OptimisticLockException と StaleObjectStateException が発生していることがわかります。

それで、この OptimisticLockException をキャッチする方法を教えてください。

4

1 に答える 1

0

すべきではありません。また、JBが言ったことに+1。この例外は、何かを伝えようとしています。子がまだ参照している間に、外部キー関係の親行を削除しようとしています。親と子とは?良い:

a foreign key constraint fails (`functionTest/MotherTable`, CONSTRAINT `FKBC6CB0E6D5545EFD` FOREIGN KEY (`MotherTable_FieldId`) REFERENCES `ChildTable` (`childTableId`))

したがって、MotherTable.MotherTableFieldId は ChildTable.childTableId を参照しています。そして、母親がまだ子を指している間に子を削除しようとしています。それはうまくいきません。

でも、どうしてこんな関係になったのか不思議です。モデルは次のようになります。

@Entity
@Table(name="MotherTable")
class Mother {
  @Id
  Long id;
  @ManyToOne
  @JoinColumn(name="MotherTable_FieldId")
  Child child;
}

@Entity
@Table(name="ChildTable"
class Child {
  @Id
  @Column(name="childTableId")
  Long id; 
  @OneToMany(mappedBy="child")
  Set<Mother> mothers;
}

あなたの子供は多くの母親を持つことができるので、これは奇妙です。代わりにこれが欲しかったのかもしれません:

@Entity
class Mother {
  @Id
  Long id;
  @OneToMany(mappedBy="mother")
  Set<Child> children;
}

@Entity
class Child {
  @Id
  Long id; 
  @ManyToOne
  @JoinColumn(name="mother_id")
  Mother mother;
}

この場合、DAO メソッドは次のようになります。

@Transactional
public void deleteFamily(Mother mother) {
  for (Child c: mother.getChildren()) {
    em.remove(c);
  }
  em.remove(mother);
}

カスケードを使用することもできます。

@Entity
class Mother {
  @Id
  Long id;
  @OneToMany(mappedBy="mother", cascading=CascadeType.ALL)
  Set<Child> children;
}

これにより、DAO メソッドが次のように簡略化されます。

@Transactional
public void deleteFamily(Mother mother) {
  em.remove(mother);
}

さらには:

@Entity
class Mother {
  @Id
  Long id;
  @OneToMany(mappedBy="mother", cascading=CascadeType.ALL, orphanRemoval=true)
  Set<Child> children;
}

em.remove()そして今、あなたは子供にする必要はありません:

@Transactional
public void deleteChild(Child child) {
  Mother m = child.getMother();
  m.getChildren().remove(child);
}

また、 でトランザクションをコミットしようとするべきではありません。これはem.flush()いくつかの点で間違っています。

  1. トランザクションはコミットされますem.getTransaction().commit()
  2. 何をしようとしているのか考えてみてください: deleteFamily は 1 つのトランザクションで発生するはずですか? はい?次に、そのように実装します。子を削除した後に部分的なコミットを実行しようとしないでください。
  3. 他の人にトランザクションを管理してもらう方がはるかに便利です。メソッドを @Transactional としてマークし、JTA フレームワークに詳細を処理させるだけです。
  4. とにかく、DAO メソッドはトランザクションを実行しようとするべきではありません。これについて考えてみてください: いくつかの DAO メソッドを使用するサービスを後で実装したいと思うかもしれません。それぞれが別々のトランザクションで自分自身をコミットしようとする場合、 toto のサービス呼び出しはトランザクションになることはできません。良くないね。したがって、DAO メソッドを再利用したい場合は、トランザクション関連のものをそれらの上にある別のレイヤーにプルします。
于 2013-04-02T17:23:04.527 に答える