システム部の福原と申します。
前回に引き続き、あーだこーだしている日常を並べていきます。
今日はSpring2.5対応のためにやっていることをまとめてみました。
ご笑覧あれー。
ChainedTransactionManager の非推奨化
Spring 2.5 から ChainedTransactionManager が非推奨になるらしい。
これは複数データベースにつなぐときに、トランザクションの同期をなるべく頑張ってくれるクラスだ。
対応しなくては、、、。
代わりは、TransactionSynchronization か、、、これどうやって使うんだろう?
<<ドタドタ。バタリ>>
うむ。最初のトランザクション中に、こう書けばよいのだな。
afterCommit()
の中で次のトランザクション処理をするメソッドを呼べば良いと。
1 2 3 4 5 6 |
// 最初のトランザクションマネージャーでの @Transactional なメソッド内で、以下のような処理をする。 TransactionSynchronizationManager.registerSynchronization(object : TransactionSynchronization { override fun afterCommit() { // 次のトランザクションマネージャーで管理されているメソッドをここで呼ぶ。 } }) |
うん、確かに複数データベース両方更新される。
あ、なんか調べてると Spring 4.2 以降は TransactionalEventListener 使えって書いてあるな。
どうやってつかうんだろ?
<<ドタドタ。バタリ>>
ええと、まず、こういうイベントクラスを用意する。
1 |
data class SampleEvent(val id: Long) |
で、最初のトランザクションの中で、ApplicationEventPublisher なフィールドに、イベントのオブジェクトを publishEvent()
すると。
1 2 |
val event = SampleEvent(123L) applicationEventPublisher.publishEvent(event) |
あとは、イベントを処理するメソッドを作る。
@TransactionalEventListener
をつければ良い。
ついでに次のトランザクションマネージャーが効くように @Transactional
にもしてしまえば良い。
1 2 3 4 5 6 |
@TransactionalEventListener @Transactional( transactionManager = "<<次のトランザクションマネージャー>>") fun processSampleEvent(event: SampleEvent) { logger.info("Event received: $event") // 次のトランザクション下でやりたいDB更新処理をする。 } |
できた。DB両方とも更新される。
あ、そういえば、エラー発生時の挙動どうなるだろう。
<<ドタドタ。バタリ>>
エラー発生時の挙動
これは、、?
どちらの実装でも先のトランザクションが終わった後に、後のトランザクションが始まるようだ。
後のトランザクションが失敗したら先のトランザクションもROLLBACKしてほしいな。
模式図を描くとこうなる。
とりあえず TransactionalEventListener での全体の動きはこうだ。

成功時はこれで良い。
だが失敗時はこういう場合もある。

データベース2へのSQL発行時になにかエラーが起きた場合、データベース1 はすでにコミットされている。
いやいやいや、こういう場合でもデータベース1 はROLLBACKしたい。
データベース1へのCOMMITをもうちょっと待ってほしいな。
その点、ChainedTransactionManager は先のトランザクションの両方のトランザクションを大体並行に操作してくれる。
データベース2へのSQL発行時にエラーが起きてもROLLBACKしてくれる。

データベース2へのCOMMIT時にエラーが起きた時にはデータベース1 はすでにコミットされていてそれはもう戻せない。
やむを得ないが弊社の要件上はその振る舞いでもやっていけるからそれで良いんだよな。
どうしよう。。。
ではDeprecatedになってしまったが、いっそChainedTransactionManager を勝手改造して使ってしまおうか。
ソースはこれだ、
ChainedTransactionManager.java
見たところ、実装はSpringの内部パッケージに依存してはいないようだ。
すると今後の内部変化の影響は受けないと思われる。
いけるかな?
Kotlinに変換して、依存するクラスとテストケースもいくつか変換して、、、
<<ドタドタ。バタリ>>
できた。動くぞ。
既存のテストケースもちゃんと動作する。
当たり前といえば当たり前だけど。
うん、こういうのが欲しい。
んー、ちょっとまって。今私は何をしているんだ?
まとめ
やっていることは、 PlatformTransactionManager を継承した自前トランザクションマネージャを作りましたということだ。
中身は丸コピだけど。
Spring フレームワークではサポートしないコードを今後は自前で管理することになるからちょっとコストがかかるな。
まあでもこれで、システムの要件に合わせますと。
さて、どうやって各システムに導入しよう?
各開発プロジェクトにこのコードをコピーして使ってもらうとなると面倒事をばらまきそうだ。
どうしよう。
天の声: Mavenのリポジトリ作ってそこにアーティファクトとして保存して、いろんなプロジェクトから使えるようにしましょう! |
あっはい。まあそうなるよね。
github packages使ってみるか。やり方調べよう。
続く!