MyBatis-Spring 1.2系(+MyBatis 3.3系)までは、Spring側で指定したトランザクションタイムアウト値が無視されていましたが、MyBatis-Spring 1.3(+MyBatis 3.4)からタイムアウト値がMyBatisが発行するクエリーのタイムアウト値に連携されるように改善されました ![]()
動作検証バージョン
- MyBatis 3.4.0
- MyBatis Spring 1.3.0
- Spring Framework 4.2.5.RELEASE
- Spring Boot 1.3.3.RELEASE
Springのトランザクションタイムアウト値ってどう使われるのか?
まず、Springのトランザクションタイムアウト値がどのように使われるのかを、Spring純正のJdbcTemplateを使った時の動作を例に説明しましょう。なお、SpringのトランザクションマネージャーにはJDBC(java.sql.Connection)のAPI(commit,rollback)を使ってトランザクションを管理するDataSourceTransactionManagerを使用します。
Note: JTAを使う場合は・・・
トランザクションマネージャーとしてJTAを使う場合は、JTAが提供しているタイムアウト検知の仕組みが利用されます。今回の記事では扱いません。
| 項番 | 説明 |
|---|---|
| ① | Controllerからトランザクション境界のメソッド(Proxyオブジェクトのメソッド)を呼び出す。Proxyオブジェクトのメソッドを呼び出すと、トランザクションの制御を行うクラス(TransactionInterceptor)が呼び出される。 |
| ② |
TransactionInterceptorは、DataSourceTransactionManagerのメソッドを呼び出してトランザクションを開始する。DataSourceTransactionManagerは、@Transactionalからタイムアウト値を読み取り、ConnectionHolder(トランザクション内で共有するjava.sql.Connectionを保持する領域)に設定する。 |
| ③ |
TransactionInterceptorは、トランザクション開始後にServiceのメソッドを呼び出す。 |
| ④ | Serviceは、JdbcTemplateのメソッドを呼び出してデータアクセスする。 |
| ⑤ |
JdbcTemplateは、DataSourceUtilsのメソッドを呼び出してjava.sql.Statementのクエリータイムアウトにトランザクションタイムアウトの残り時間を設定する。 |
| ⑥ |
JdbcTemplateは、Statementのメソッドを呼び出してSQLの実行を依頼する。 |
| ⑦ |
Statementは、データベースにSQLを実行する。 |
MyBatis-Spring 1.3はどうやってSpringのトランザクションタイムアウト値を連携しているのか?
基本的な流れはSpring純正のJdbcTemplate使用時と同じですが、MyBatisの場合は、MyBatisが独自に提供しているトランザクションを抽象化するインタフェース(org.apache.ibatis.transaction.Transaction)から取得したタイムアウト値をStatementに設定する仕組みになっています。タイムアウト値を取得するためのメソッド(getTimeout)はMyBatis 3.4から追加されたメソッドで、MyBatis-Spring 1.3からこのメソッドが実装されています。
MyBatis側のクエリータイムアウト値はどう扱われるのか?
MyBatisには、クエリータイムアウト値を指定する仕組みがもともと用意されており、設定値はSQL単位に指定することができます。(省略した場合はMyBatisの設定で指定したデフォルト値が適用されます)
Springのトランザクション管理下でMyBatis側にクエリータイムアウト値が指定されていた場合は、トランザクションタイムアウトの残り時間より短い場合は、MyBatis側の設定が有効になります。
トランザクションコミット時のタイムアウト検知について
クエリーの発行時にはトランザクションタイムアウトにならなかったが、コミットする時にトランザクションタイムアウトになっているケースはどのような動作になるのでしょうか?
すくなくても投稿時点(2016/4/30時点)では、DataSourceTransactionManagerを使う場合はタイムアウトは検知できません。この事象の詳細や対処方法については、@yoshidan さんの記事「Spring Bootとmybatisでトランザクションタイムアウトが効かない」をご覧ください。
まとめ
@Transactionalで指定したタイムアウト値がMyBatis側にも連携されていると勘違いしているエンジニアは多いのではないのでしょうか?(直感的には連携されてほしいし、私自身も連携されているものだと決めつけていた一人です・・・
)
MyBatis-Springユーザーの方は、ぜひMyBatis-Spring 1.3 + MyBatis 3.4へのバージョンアップを検討してみてください。

