Spring Boot 1.5からPlatformTransactionManager用のCustomizerが追加される

Spring Bootを使うと、利用技術に対応した`PlatformTransactionManager`インタフェースの実装クラス(`DataSourceTransactionManager`, `JpaTransactionManager`, `JtaTransactionManager`, etc...)のBean定義が自動で行われます。デフォのまま使えるか？というとちょっと微妙で、いくつかカスタマイズした方がよいパラメータがあると私は思っています。そんなこんなで・・・今回は、Spring Boot上での`PlatformTransactionManager`のカスタマイズ方法を紹介します。


# Spring Boot 1.4までは？

Spring Boot 1.4までは、`PlatformTransactionManager`をカスタマイズしたい場合は、以下のいずれかの方法で行う必要がありました。

* Spring Bootの自動定義は使わずに`@Bean`メソッドを用意してカスタマイズする方法（正攻法）

```java
@Bean
PlatformTransactionManager transactionManager(DataSource dataSource) {
	DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
	transactionManager.setDefaultTimeout(30);
	transactionManager.setRollbackOnCommitFailure(true);
	// ...
	return transactionManager;
}
```

* `@Autowired`メソッドを用意してSpring Bootが定義したBeanをカスタマイズする方法

```java
@Autowired
void configureTransactionManager(AbstractPlatformTransactionManager transactionManager) { // Spring Bootが自動設定したBeanがインジェクションしてカスタマイズ
	transactionManager.setDefaultTimeout(30);
	transactionManager.setRollbackOnCommitFailure(true);
	// ...
}
```

# Spring Boot 1.5(1月末リリース予定)からは？

上記方法に加え、Spring Boot 1.5からは、`org.springframework.boot.autoconfigure.transaction.PlatformTransactionManagerCustomizer`の実装クラスのBeanをDIコンテナに登録することでカスタマイズすることができるようになります。

```java:PlatformTransactionManagerCustomizerの抜粋
public interface PlatformTransactionManagerCustomizer<T extends PlatformTransactionManager> {
	void customize(T transactionManager);
}
```

この仕組みは、複数のアプリケーションでカスタマイズロジックを共有したい場合に役にたつ仕組みだと思います。

# PlatformTransactionManagerCustomizerの組み込み実装

実は・・・Spring Bootは、`PlatformTransactionManagerCustomizer`インタフェースを実装したクラスを組み込みで提供しています。Spring Bootが提供している組み込み実装を利用すると、`AbstractPlatformTransactionManager`に定義されている以下のプロパティの値をカスタマイズすることができます。([gh-7561](https://github.com/spring-projects/spring-boot/pull/7561))


* デフォルトのトランザクションタイムアウト時間(秒)
* コミット時にエラーが発生した際にロールバック処理を行うか否かのフラグ(デフォルト値はfalse=ロールバックメソッドは呼び出さない)

```properties:src/main/resources/application.properties
spring.transaction.default-timeout=30
spring.transaction.rollback-on-commit-failure=true
```

> **Note:**
>
> Springのトランザクションタイムアウト値の扱いについては「[MyBatis-Spring 1.3からSpringのトランザクションタイムアウト値が連携される！！（Springのトランザクションタイムアウト値ってどう使われるのか？）](http://qiita.com/kazuki43zoo/items/1ac208a49c1d6ceeb0b8#spring%E3%81%AE%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%A2%E3%82%A6%E3%83%88%E5%80%A4%E3%81%A3%E3%81%A6%E3%81%A9%E3%81%86%E4%BD%BF%E3%82%8F%E3%82%8C%E3%82%8B%E3%81%AE%E3%81%8B) 」を、「コミット時にエラーが発生した際にロールバック処理を行うか否かのフラグ」の必要性については「[SpringのDataSourceTransactionManagerを使うとエラー時にCommitされる可能性あり！？](http://qiita.com/kazuki43zoo/items/73225b41a2c36a720a4b)」をごらんください。


# PlatformTransactionManagerCustomizerの実装クラスを作成してカスタマイズする方法

複数のアプリケーションでカスタマイズロジックを共有したい場合は、`PlatformTransactionManagerCustomizer`の実装クラスを作成してカスタマイズするのがようでしょう。（共通ライブラリみたいな感じで提供する場合をイメージ）

```java
@Component
public class MyPlatformTransactionManagerCustomizer
		implements PlatformTransactionManagerCustomizer<AbstractPlatformTransactionManager> {
	@Override
	public void customize(AbstractPlatformTransactionManager transactionManager) {
		// ... カスタマイズロジックを実装する
	}
}
```

> **Note:**
>
> Spring Bootが生成した`PlatformTransactionManager`が`PlatformTransactionManagerCustomizer`のジェネリクスに指定した型に割り当てることができる場合に限り、`customize`メソッドが呼び出される仕組みになっています。


# Lambda式を使用してカスタマイズする方法 (Java SE 8+)

アプリケーション固有のカスタマイズについては、上記のようなクラスを作らずに、Java SE 8でサポートされたLambda式を使って`PlatformTransactionManagerCustomizer`の`customize`メソッドをアドホックに実装することもできます。

```java
@Bean
PlatformTransactionManagerCustomizer<AbstractPlatformTransactionManager> transactionManagerCustomizer() {
	return transactionManager -> {
		// ... カスタマイズロジックを実装する
	};
}
```

> ~~**Warning:**~~
>
> ~~Lambda式を使うと・・・`PlatformTransactionManagerCustomizer`のジェネリクスに指定した型による`customize`メソッドの呼び出し制御が効きません（＝ジェネリクスに`PlatformTransactionManager`を指定しているとみなされてしまう・・・）。~~
> ~~そのため、Spring Bootが生成した`PlatformTransactionManager`がジェネリクスに指定した型に割り当てできないと・・・・`CalssCastException`が発生するので注意してください！！~~
>
> **12/30 15:40 追加**
>
> `ClassCastException`が発生したら無視するように対応してくれたので、クラスを作成した時と同じ動作になります！！！

# PlatformTransactionManagerCustomizerの適用順番

`PlatformTransactionManagerCustomizer`は、Spring Bootの組み込み実装(`TransactionProperties`)を含め、複数適用することができます。複数の`PlatformTransactionManagerCustomizer`をDIコンテナに登録した時の動作は、デフォルトでは不定です。順番を制御したい場合は・・・・

* `@org.springframework.core.annotation.Order`を付与
* `org.springframework.core.Ordered`インタフェースを実装

することで実現することができますが、Spring Bootの組み込み実装(`TransactionProperties`)より後にすることはできないみたいです。（少なくても・・・私の環境では組み込み実装より後にすることはできませんでしたが、困ることはない気がしてます）

# まとめ

まだリリースされていませんが、Spring Boot 1.5より、`PlatformTransactionManager`をカスタマイズをサポートするインタフェース(`PlatformTransactionManagerCustomizer`)が追加されたよ〜

ということと・・・

カスタマイズ頻度が多そうな以下のプロパティが、`application.properties`(or `application.yml`)でカスタマイズ可能になったよ〜

* デフォルトのトランザクションタイムアウト時間(秒)
* コミット時にエラーが発生した際にロールバック処理を行うか否かのフラグ

ということを紹介しました。

Spring Boot 1.5は年明けにRC1、1月末に正式版がリリースされる予定になっています。RC1が出たらSpring Boot 1.4からの変更点を調べようかな〜（と思っています）。

# さいごに

たぶんこのエントリーが今年のQiitaの書き納めかな〜と思っています。
来年も気が向くままに投稿しようかな〜と思っているので、すこしでも皆様の役に立てれば幸いです。（へんなところや、もっとこうした方がいいよ〜とかあれば是非コメントください！！）

では、今年も残りあと２日ですが・・・よいお年を〜 :see_no_evil: :hear_no_evil: :speak_no_evil: