20

2つの方法が異なるステートレスBeanに存在するシナリオを考えてみましょう

public class Bean_A {
   Bean_B beanB; // Injected or whatever
   public void methodA() {
    Entity e1 = // get from db
    e1.setName("Blah");
    entityManager.persist(e1);
    int age = beanB.methodB();

   }
} 
public class Bean_B {
  //Note transaction
  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
   public void methodB() {

    // complex calc to calculate age  
  }

}

BeanA.methodAによって開始されたトランザクションは一時停止され、BeanB.methodBで新しいトランザクションが開始されます。methodBがmethodAによって変更された同じエンティティにアクセスする必要がある場合はどうなりますか。これによりデッドロックが発生しますが、分離レベルに依存せずにデッドロックを防ぐことはできますか?

4

4 に答える 4

25

うーん、すべてのケースをリストしましょう。

REQUIRES_NEWトランザクションを実際にネストするわけではありませんが、前述のように、現在のトランザクションを一時停止します。その場合、同じ情報にアクセスするトランザクションは2つだけです。(これは、同時ではなく同じ実行スレッドにあることを除いて、2つの通常の同時トランザクションに似ています)。

T1 T2          T1 T2
―              ―
|              |
               |
   ―           |  ―
   |           |  |
   |     =     |  |
   ―           |  ―
               |
|              |
―              ―

次に、楽観的ロックと悲観ロックを検討する必要があります。

また、ORMによって操作されるフラッシュを考慮する必要があります。ORMでは、フレームワークによって制御されるため、書き込みが発生するタイミングを明確に制御することはできませんflush。通常、コミットの前に1つの暗黙的なフラッシュが発生しますが、多くのエントリが変更された場合、フレームワークは中間フラッシュも実行できます。

1)読み取りはロックを取得せず、書き込みは排他的ロックを取得する楽観的ロックについて考えてみましょう。

T1による読み取りはロックを取得しません。

1a)T1が変更を時期尚早にフラッシュした場合でも、排他ロックを取得しました。T2がコミットすると、ロックを取得しようとしますが、取得できません。システムがブロックされています。これは、特定の種類のデッドロックの可能性があります。完了は、トランザクションまたはロックがどのようにタイムアウトするかによって異なります。

1b)T1が変更を時期尚早にフラッシュしなかった場合、ロックは取得されていません。T2がコミットすると、T2はそれを取得して解放し、成功します。T1がコミットしようとすると、競合に気づき失敗します。

2)読み取​​りが共有ロックを取得し、排他ロックを書き込むという悲観的なロックについて考えてみましょう。

T1による読み取りは、共有ロックを取得します。

2a)T1が時期尚早にフラッシュされた場合、それはロックを排他的ロックに変えます。状況は1a)と同様です。

2b)T1が時期尚早にフラッシュしなかった場合、T1は共有ロックを保持します。T2がコミットすると、排他ロックとブロックを取得しようとします。システムは再びブロックされます。

結論:厳密に制御できない時期尚早のフラッシュが発生しない場合は、楽観的ロックで問題ありません。

于 2012-05-30T22:28:06.813 に答える
3

エンティティを渡し、マージします...

新しいエンティティをに渡し、methodB()それを新しいにマージできますEntityManager。メソッドが戻ったら、エンティティを更新して変更を確認します。

public class Bean_A {
  Bean_B beanB; // Injected or whatever
  public void methodA() {
    Entity e1 = // get from db
    e1.setName("Blah");
    entityManager.persist(e1);
    int age = beanB.methodB(e1);
    entityManager.refresh(e1);
  }
} 

public class Bean_B {
  //Note transaction
  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void methodB(Entity e1) {
    e1 = entityManager.merge(e1);
    // complex calc to calculate age  
  }

}

これにより、新しいトランザクションが。の後に終了したときにエンティティがコミットされることに注意してくださいmethodB

...またはmethodBを呼び出す前に保存します

上記の方法を使用する場合、エンティティはメイントランザクションとは別に保存されるため、Bean_A呼び出す前に保存しても何も失われませんmethodB()

public class Bean_A {
  Bean_B beanB; // Injected or whatever

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void createEntity() {
    Entity e1 = // get from db
    e1.setName("Blah");
    entityManager.persist(e1);
  }

  public void methodA() {
    createEntity()   
    int age = beanB.methodB();
  }
} 

public class Bean_B {
  //Note transaction
  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void methodB() {

    // complex calc to calculate age  
  }

}
于 2012-06-03T01:34:47.523 に答える
1

これは、トランザクション境界の使用に関する最近の記事です。REQUIRES_NEW

私の経験から、標準コードでデッドロックが発生することはないはずです。つまり、制限where句があり、挿入が少ないクエリです。場合によっては、トランザクション中に1つのテーブルで多くの行が読み取られたり挿入されたりすると、一部のデータベースエンジンがロックエスカレーションを実行することがあります。その場合、デッドロックが発生する可能性があります。

しかし、その場合、問題はSQL設計に起因するのではREQUIRES_NEWなく、SQL設計に起因します。その設計を改善できない場合は、分離レベルをより緩いレベルに変更する他の選択肢はありません。

于 2012-05-30T21:48:39.780 に答える
0

entityManager.persist(e1); 前後にプログラムでトランザクションをコミットすることによってint age = beanB.methodB();

public class Bean_A {
   Bean_B beanB; // Injected or whatever
   public void methodA() {
    EntityManager em = createEntityManager();
    Entity e1 = // get from db
    e1.setName("Blah");
    entityManager.persist(e1);
    em.getTransaction().commit();
    int age = beanB.methodB();

   }
} 
public class Bean_B {
  //Note transaction
  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
   public void methodB() {

    // complex calc to calculate age  
  }

}

編集CMT

CMTを使用している場合でも、プログラムでコミットできます。からトランザクションを取得するだけですEJBContext。例: http: //geertschuring.wordpress.com/2008/10/07/how-to-use-bean-managed-transactions-with-ejb3-jpa-and-jta/

または、を追加することもできます@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void methodC()e1.setName("Blah"); entityManager.persist(e1);つまり、トランザクションでe1を永続化します。その後、あなたmethodA()は電話します

methodC();
beanB.methodB(); 
于 2012-05-24T11:52:02.963 に答える