40475
JavaEE7で開発しています。  
デッドロックが検出された場合、`SQLTransactonRollbackException`がスローされることは分かっているのですが、これを捕まえてトランザクションをリトライする方法が分かりません。

トランザクション制御には、`@Transactional`を使っています。  
ただ、`@Transactional`のついたクラス（またはメソッド）の中でロールバックされた場合、再びそのメソッドを呼ぶと、`TransactionRequiredException`が発生してしまいます。

トランザクションの外側（呼び出し側）のコードのイメージは↓こんな感じです。  
（リトライ回数の制御とか、かなり省いていますが）

```

    @RequestScoped
    public class SomeFacade {
      @Inject
      private SomeTran someTran;
    
      public void run()
        try {
          this.someTran.execute();
        } catch (SQLTransactonRollbackException ex) {
          this.someTran.execute(); // -> TransactionRequiredException
        }
      }
    }
    
```

`SomeTran`クラスに`@RequestScoped`と`@Transactional`が付いています。

```

    @RequestScoped
    public class SomeTran {
      @Transactional
      public void execute() {
          ...
      }
    }
    
```

1回のリクエストで、トランザクションを2度実行することはできないのでしょうか？  
（そんなことは無いと思いますが・・・）
CC BY-SA 3.0
2017-11-07T10:28:01.330
39377
2017-12-22T06:29:24.567
2017-11-09T00:00:47.117
8078
8078
question
java java-ee
JavaEEでトランザクションのリトライをしたい
試していませんが、`@Transactional`を付けたメソッドの呼び出し元で`SQLTransactonRollbackException`を`catch`して再実行したら、うまくいきませんか？

```

    @RequestScoped
    public class SomeFacade {
      @Inject
      private SomeTran someTran;
    
      public void run()
        try {
          execute();
        } catch (SQLTransactonRollbackException ex) {
          execute();
        }
      }
    
      @Transactional
      private void execute()
          this.someTran.execute();
      }
    }
    
```
CC BY-SA 3.0
2017-11-07T20:17:35.520
39398
2017-11-07T20:17:35.520
21092
39377
answer
[`@Transactional`の`value`パラメータのデフォルト値](https://docs.oracle.com/javaee/7/api/javax/transaction/Transactional.html#value--)は[`REQUIRED`](https://docs.oracle.com/javaee/7/api/javax/transaction/Transactional.TxType.html#REQUIRED)です。  
`REQUIRED`は、トランザクションの外側で実行する場合にはトランザクションを新しく開始しますが、トランザクション内で実行する場合はそのトランザクションを利用します。

つまり、質問文のコードの`execute()`呼び出しは1回目も2回目も同一トランザクションで実行されます(`execute()`呼び出し前に、既にトランザクションは開始されているのだと思われます)。  
そして1回目の呼び出し中に例外を送出しロールバックフラグが立っているので2回目の呼び出し内の処理は実行できません。

デフォルトの`REQUIRED`を用いるのではなく、明示的に[`REQUIRES_NEW`](https://docs.oracle.com/javaee/7/api/javax/transaction/Transactional.TxType.html#REQUIRES_NEW)を指定すれば、それぞれの`execute()`は新しい(独立した)トランザクションで実行されるので今回の例外は避けられるでしょう。  
ただし、そのようなトランザクション境界の設定が本当に正しいのかどうかは要件によります。  
(上記のような指定を行った場合、`execute()`の外側の処理は、`execute()`とは異なるトランザクションで実行されます。)

* * *

以下、参考になりそうなリンクを記載します:

  * [The Java EE 7 TutorialのTransactionsの章をテキトーに訳した - kagamihogeの日記](http://kagamihoge.hatenablog.com/entry/2014/08/27/202902)
    * 原文はURLが変わっており図表がリンク切れになっているようです。[新しいURLはこちら](https://docs.oracle.com/javaee/7/tutorial/transactions.htm#BNCIH)でした。
    * [51.3.1 Transaction Attributes](https://docs.oracle.com/javaee/7/tutorial/transactions003.htm#BNCIK)節が今回関係する箇所です。
  * [JavaEE7をはじめよう(16) - CDI トランザクション - エンタープライズギークス (Enterprise Geeks)](http://enterprisegeeks.hatenablog.com/entry/2015/09/18/120844)
CC BY-SA 3.0
2017-12-22T06:29:24.567
40475
2017-12-22T06:29:24.567
2808
39377
answer