SpringのDBコネクションの共有方法(DBトランザクション)を理解する

今回は、SpringのDBコネクションの共有方法（＝複数のSQLを同一トランザクション内で実行する方法）の仕組みについて説明します。
仕組みだけ説明をするとSpringに詳しくない人に対してハードルが高くなる！？気がするので・・・Spring提供のDBアクセス機能(`JdbcTemplate`）を実際に使いながら、SpringがDBコネクションをどうやって生成・共有するのか説明していきたいと思います。

# 動作検証バージョン

* Spring Boot 1.5.1.RELEASE
* Spring Framework 4.3.6.RELEASE


# 検証用のSpring Bootプロジェクトの作成

まず、検証用のSpring Bootプロジェクト作成しましょう(Dependencieにはjdbcとh2を指定)。ここではコマンドラインでプロジェクトを作成する例になっていますが、[SPRING INITIALIZRのWeb UI](https://start.spring.io/)やお使いのIDEの機能で生成しても（もちろん）OKです！！

```
$ curl -s https://start.spring.io/starter.tgz\
       -d name=spring-tx-demo\
       -d artifactId=spring-tx-demo\
       -d dependencies=jdbc,h2\
       -d baseDir=spring-tx-demo\
       | tar -xzvf -
```

プロジェクトを生成すると、以下のような構成のMavenプロジェクトが生成されます。

```
$ cd spring-tx-demo
$ tree
.
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── SpringTxDemoApplication.java
    │   └── resources
    │       └── application.properties
    └── test
        └── java
            └── com
                └── example
                    └── SpringTxDemoApplicationTests.java
```

必要に応じてお使いのIDE上にインポートしてください！！

# データベース(データソース)のセットアップ

今回はH2のインメモリDBを使用して検証します。

```sql:src/main/resources/schema.sql
CREATE TABLE account (
  id CHAR(10) PRIMARY KEY,
  name VARCHAR(255)
);
```

また、Spring Bootが内蔵している`DataSource`（コネクションプール付きの`DataSource`)を使用するとトランザクション制御が正しく行われているか検証するのが難しいので、検証用にコネクションプールなしの`DataSource`をBean定義します。

> **Note:**
>
> 通常のアプリケションを開発する際は、コネクションプールを使ってくださいね！！（↓のようなBean定義は不要です）

```java:src/main/java/com/example/SpringTxDemoApplication.java
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

import javax.sql.DataSource;
import java.sql.Driver;
import java.util.Properties;

@SpringBootApplication
public class SpringTxDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringTxDemoApplication.class, args);
	}
	
	@Configuration
	static class DataSourceConfiguration {
		@Bean
		public DataSource dataSource(DataSourceProperties properties) throws ClassNotFoundException {
			// ★★★ Springが提供しているコネクションをプールしない実装クラスを利用 ★★★
			SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
			dataSource.setDriverClass((Class<? extends Driver>) Class.forName(properties.determineDriverClassName()));
			dataSource.setUrl(properties.determineUrl());
			dataSource.setUsername(properties.determineUsername());
			dataSource.setPassword(properties.determinePassword());
			Properties connectionProperties = new Properties();
			connectionProperties.setProperty("autoCommit", "false"); // ★★★ 自動コミットをOFFにする ★★★
			dataSource.setConnectionProperties(connectionProperties);
			return dataSource;
		}
	}

}
```

また、自動コミットフラグは必ず`false`(自動コミットしない)に設定してください。自動コミットフラグを`true`(自動コミットする)にしてしまうと、実際だれがコミットしたのか判断できないので・・・。

# `JdbcTemplate`を使用したDBアクセス

`SpringTxDemoApplication`クラスに`CommandLineRunner`インタフェースを実装し、`run`メソッドの中で`JdbcTemplate`を使用したDBアクセスを実装しましょう。

> **Note:**
>
> Springが提供するDBアクセス機能のログ(デバッグログ)を出力するようにしておくと、内部でどのような動きをしているか確認しやすくなります。
>
> ```properties:src/main/resources/application.properteis
> logging.level.org.springframework.jdbc=debug
> ```


```java:src/main/java/com/example/SpringTxDemoApplication.java
package com.example;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

import javax.sql.DataSource;
import java.sql.Driver;
import java.util.Map;
import java.util.Properties;

@SpringBootApplication
public class SpringTxDemoApplication implements CommandLineRunner {
	
	public static void main(String[] args) {
		SpringApplication.run(SpringTxDemoApplication.class, args);
	}
	
	private final JdbcTemplate jdbcTemplate;
	
	public SpringTxDemoApplication(JdbcTemplate jdbcTemplate){
		this.jdbcTemplate = jdbcTemplate;
	}

	// ★★★ JdbcTemplateを使用したDBアクセス処理を実装する ★★★
	@Override
	public void run(String... args) throws Exception {
		
		jdbcTemplate.update("INSERT INTO city (name, state, country) VALUES ('San Francisco', 'CA', 'US')");
		
		Map<String, Object> city = jdbcTemplate.queryForMap("SELECT id, name, state, country FROM city WHERE id = 1");
		
		System.out.println(city);
		
	}
	
	@Configuration
	static class DataSourceConfiguration {
		@Bean
		public DataSource dataSource(DataSourceProperties properties) throws ClassNotFoundException {
			SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
			dataSource.setDriverClass((Class<? extends Driver>) Class.forName(properties.determineDriverClassName()));
			dataSource.setUrl(properties.determineUrl());
			dataSource.setUsername(properties.determineUsername());
			dataSource.setPassword(properties.determinePassword());
			Properties connectionProperties = new Properties();
			connectionProperties.setProperty("autoCommit", "false");
			dataSource.setConnectionProperties(connectionProperties);
			return dataSource;
		}
	}
	
}
```

では、このクラスをSpring Bootアプリとして実行すると、どういう結果になるでしょうか？
1件レコードをINSERTして、INSERTしたレコードを取得しているので・・・

コンソールには・・・

```
{ID=1, NAME=San Francisco, STATE=CA, COUNTRY=US}
```

と出力されることを期待すると思いますが・・・・

実際に実行してみると、INSERTしたレコードを取得する際に以下のエラーが発生します :cold_sweat:


```
./mvnw spring-boot:run
...
 .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.1.RELEASE)
...
2017-02-12 17:26:08.627 ERROR 46138 --- [           main] o.s.boot.SpringApplication               : Application startup failed

java.lang.IllegalStateException: Failed to execute CommandLineRunner
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:779) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:760) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
	at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:747) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
	at com.example.SpringTxDemoApplication.main(SpringTxDemoApplication.java:21) [classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
	at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:527) [spring-boot-maven-plugin-1.5.1.RELEASE.jar:1.5.1.RELEASE]
	at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]
Caused by: org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
	at org.springframework.dao.support.DataAccessUtils.requiredSingleResult(DataAccessUtils.java:71) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
	at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:495) ~[spring-jdbc-4.3.6.RELEASE.jar:4.3.6.RELEASE]
	at org.springframework.jdbc.core.JdbcTemplate.queryForMap(JdbcTemplate.java:489) ~[spring-jdbc-4.3.6.RELEASE.jar:4.3.6.RELEASE]
	at com.example.SpringTxDemoApplication.run(SpringTxDemoApplication.java:35) [classes/:na]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:776) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
	... 12 common frames omitted
...
```

原因例外をみると、「`Caused by: org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0`」となっているので、レコードがみつからなかったことが原因のようです。

# `JdbcTemplate`利用時のConnectionの扱い

では、なぜ直前にINSERTしたはずのレコードがみつからなかったのでしょうか？
以下の図は、Springのトランザクション管理外で`JdbcTemplate`のメソッドを呼び出した場合に、どのように`Connection`を取得しているか示したものです。

![spring-jdbc-non-tx.png](https://qiita-image-store.s3.amazonaws.com/0/117313/e281a233-8c46-3f4c-2531-9312c0b156a1.png)

| 項番 | 説明 |
| :---: | --- |
| ① | ControllerからSpringのトランザクション管理外のServiceクラスのメソッドを呼び出す。|
| ② | Serviceから`JdbcTemplate`のメソッドを呼び出す。 |
| ③ | `JdbcTemplate`は、自身に割り当てられている`DataSource`から`Connection`を取得する。その際、直接`DataSource`から`Connection`を取得するのではなく、`DataSourceUtil#getConnection(DataSource)`を介して取得する仕組みになっています。後で詳しく説明しますが、トランザクション管理下での`Connection`の共有を実現しているのが`DataSourceUtil`のメソッドになります。  |
| ④ | `DataSourceUtil`から取得した`Connection`のメソッドを使用してSQLの実行を依頼する。 |
| ⑤ | `Connection`(`Connection`から取得した`Statement`)は、データベースにSQLを実行する。 |

図中の吹き出しで説明しているように、`JdbcTemplate`のメソッド呼び出しごとに別の`Connection`が使われるため、INSERT時に使用した`Connection`がコミットされないと、後続のSELECT時に使う`Connection`からはINSERTしたレコードをみつけることはできません。（ちなみに・・・これは`Connection`の分離レベル=Isolation LevelがREAD COMMITTEDの時の動きなので、分離レベルによってはレコードをみつけることができる点を補足しておきます）

ためしに・・・自動コミットフラグを`true`にして再度実行してみてください。

```java
@Bean
public DataSource dataSource(DataSourceProperties properties) throws ClassNotFoundException {
	SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
	// ...
	connectionProperties.setProperty("autoCommit", "true"); // ★★★ trueに変更 ★★★
	dataSource.setConnectionProperties(connectionProperties);
	return dataSource;
}
```

```
$ ./mvnw spring-boot:run
...
2017-02-12 18:15:25.097 DEBUG 46292 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing SQL update [INSERT INTO city (name, state, country) VALUES ('San Francisco', 'CA', 'US')]
2017-02-12 18:15:25.098 DEBUG 46292 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
2017-02-12 18:15:25.098 DEBUG 46292 --- [           main] o.s.j.datasource.SimpleDriverDataSource  : Creating new JDBC Driver Connection to [jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE]
2017-02-12 18:15:25.100 DEBUG 46292 --- [           main] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
2017-02-12 18:15:25.103 DEBUG 46292 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource
2017-02-12 18:15:25.103 DEBUG 46292 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing SQL query [SELECT id, name, state, country FROM city WHERE id = 1]
2017-02-12 18:15:25.103 DEBUG 46292 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
2017-02-12 18:15:25.103 DEBUG 46292 --- [           main] o.s.j.datasource.SimpleDriverDataSource  : Creating new JDBC Driver Connection to [jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE]
2017-02-12 18:15:25.123 DEBUG 46292 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource
{ID=1, NAME=San Francisco, STATE=CA, COUNTRY=US}
2017-02-12 18:15:25.126  INFO 46292 --- [           main] com.example.SpringTxDemoApplication      : Started SpringTxDemoApplication in 1.636 seconds (JVM running for 5.305)
...
```

上記のようなログが出力され、SELECTに成功します。これは、自動コミットフラグを`true`にすることで、INSERT成功時に`Connection`がコミットされたためです。

じゃ〜自動コミットフラグを`true`にすれば一件落着かというと・・・当然ながらそんなことはありません :sweat_smile:
説明する必要もないと思いますが・・・以下のように複数の更新操作を行う場合は、すべての更新操作が成功した場合にコミット、一部の更新操作が失敗した場合はロールバックする（＝いわゆるトランザクション制御を行う）必要がでてきます。こういったケースでは、SQLの実行毎にコミットが行われてしまう「自動コミットフラグ=`true`」を使うわけにはいかないのです。

```java
@Override
public void run(String... args) throws Exception {
	
	jdbcTemplate.update("INSERT INTO city (name, state, country) VALUES ('San Francisco', 'CA', 'US')");
	jdbcTemplate.update("INSERT INTO city (name, state, country) VALUES ('豊島区', '東京', 'JPN')");
	
	// ...	
}
```

# トランザクション管理下での`JdbcTemplate`利用時のConnectionの扱い

複数の更新操作を同一トランザクション内で行うためにはどうすればいいのか？というと・・・Springユーザならご存知のとおり、トランザクション管理下で実行したいクラスやメソッドに`@Transactional`を付与するだけです。

> **Note:**

> Spring Bootを使う場合は特別な設定は不要ですが・・・非Spring Bootの場合は、
>
> * [`@EnableTransactionManagement`の付与](http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#tx-decl-explained)
> * [`PlatformTransactionManager`(DBトランザクションの場合は`DataSourceTransactionManager`)のBean定義](http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#jdbc-DataSourceTransactionManager)
>
> が必要になります。（本エントリーでは非Spring Bootの説明は割愛させて頂きます）

では、実際に検証アプリに`@Transactional`を付与して実行してみましょう。（なお、自動コミットフラグを`false`に戻すことを忘れずに！！）

```java
@Transactional // ★★★ メソッドに付与 ★★★
@Override
public void run(String... args) throws Exception {
	
	jdbcTemplate.update("INSERT INTO city (name, state, country) VALUES ('San Francisco', 'CA', 'US')");
	
	Map<String, Object> city = jdbcTemplate.queryForMap("SELECT id, name, state, country FROM city WHERE id = 1");
	
	System.out.println(city);
	
}
```

`@Transactional`を付与した後にSpring Bootアプリを実行すると、SELECTも成功するようになりました :grin:

```
$ ./mvnw spring-boot:run
...
2017-02-12 18:42:46.689 DEBUG 46340 --- [           main] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.example.SpringTxDemoApplication$$EnhancerBySpringCGLIB$$7f2a8641.run]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2017-02-12 18:42:46.690 DEBUG 46340 --- [           main] o.s.j.datasource.SimpleDriverDataSource  : Creating new JDBC Driver Connection to [jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE]
2017-02-12 18:42:46.690 DEBUG 46340 --- [           main] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [conn1: url=jdbc:h2:mem:testdb user=SA] for JDBC transaction
2017-02-12 18:42:46.697 DEBUG 46340 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing SQL update [INSERT INTO city (name, state, country) VALUES ('San Francisco', 'CA', 'US')]
2017-02-12 18:42:46.700 DEBUG 46340 --- [           main] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
2017-02-12 18:42:46.703 DEBUG 46340 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing SQL query [SELECT id, name, state, country FROM city WHERE id = 1]
{ID=1, NAME=San Francisco, STATE=CA, COUNTRY=US}
2017-02-12 18:42:46.721 DEBUG 46340 --- [           main] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2017-02-12 18:42:46.722 DEBUG 46340 --- [           main] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [conn1: url=jdbc:h2:mem:testdb user=SA]
2017-02-12 18:42:46.722 DEBUG 46340 --- [           main] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [conn1: url=jdbc:h2:mem:testdb user=SA] after transaction
2017-02-12 18:42:46.722 DEBUG 46340 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource
..
```

ログをみてみると・・・コネクションの生成(`Creating new JDBC Driver Connection to [jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE]`)のログ出力が１回だけになり、`DataSourceTransactionManager`のログ（トランザクション開始ログなど）が出力されるようになりました。ログを見る限りだと、複数の操作でコネクションが共有されているようです。

では、なぜコネクションが共有されるようになったのか？を紐解いていきましょう。
以下の図は、Springのトランザクション管理下で`JdbcTemplate`のメソッドを呼び出した場合に、どのように`Connection`を生成し利用しているかを示したものです。
ちょっと複雑ですが、ひとつひとつ見ていけばきっと理解できると思います！！（わたしの説明がわかりずらい・・・とかはあるかも:sweat_smile:）

![spring-jdbc-tx.png](https://qiita-image-store.s3.amazonaws.com/0/117313/2e8f6ab1-da65-4a16-4b86-e9782da1a65f.png)

| 項番 | 説明 |
| :---: | --- |
| ① | ControllerからSpringのトランザクション管理下のServiceクラスのメソッドを呼び出す。トランザクション管理下のServiceクラスのメソッドを呼び出すと、実際にはProxyオブジェクトのメソッドが呼び出され、トランザクション制御を行うクラス(TransactionInterceptor)が呼び出される仕組みになっています。|
| ② | `TransactionInterceptor`は、`DataSourceTransactionManager`のメソッドを呼び出してトランザクションの開始を依頼する。|
| ③ | `DataSourceTransactionManager`は、自身に割り当てられた`DataSource`から`Connection`(`autoCommit`は`false`となる)を取得し`ConnectionHolder`(トランザクション内で共有する`Connection`を保持する領域)を生成する。生成した`ConnectionHolder`は、`TransactionSynchronizationManager`クラス上のスレッドローカルな`Map`型の変数に格納されます（`Map`に格納する際のキーとして`DataSource`インスタンスが利用されます）。|
| ④ | `TransactionInterceptor`は、トランザクション開始後にServiceのメソッドを呼び出す。|
| ⑤ | Serviceから`JdbcTemplate`のメソッドを呼び出す。 |
| ⑥ | `JdbcTemplate`は、`DataSourceUtil#getConnection(DataSource)`を介してトランザクションに割り当てられている`Connection`を取得し、取得した`Connection`のメソッドを使用してSQLの実行を依頼する。`DataSourceUtil`は、`TransactionSynchronizationManager`クラス上のスレッドローカルな`Map`型の変数から`ConnectionHolder`を取得する際のキーとして引数で受け取った`DataSource`インスタンスを指定します。そのため、`JdbcTemplate`と`DataSourceTransactionManager`に指定する`DataSource`は、同一インスタンスのものを指定する必要があります。 |
| ⑦ | `Connection`(`Connection`から取得した`Statement`)は、データベースにSQLを実行する。 |

つまり・・・トランザクションの開始時に`DataSource`から`Connection`をひとつ取得し、以降の処理では`DataSourceUtil `(`TransactionSynchronizationManager`)クラスのメソッドを介して`Connection`を共有することで、複数の操作を同一トランザクション内で実行する仕組みを提供しているのです。（**③と⑥がポイント**）

図には記載していませんが、Serviceクラスのメソッド呼び出しが終了すると、`TransactionInterceptor`から`DataSourceTransactionManager`の`commit`メソッド又は`rollback`メソッドが呼び出され、トランザクションに割り当てられている`Connection`の`commit`メソッド又は`rollback`メソッドが呼び出される仕組みになっています。


# JDBC以外のトランザクションは？

JDBC以外にも、JMS(Java Message Service)、JTA(Java Transaction API)、JPA(Java Persistence API)など様々なインフラストラクチャ用の`PlatformTransactionManager`がSpringやSpringのサブプロジェクトから提供されています。複数の操作を同一トランザクション内で実行するための仕組みはそれぞれ異なりますが、JDBCと同様にトランザクション制御に必要なオブジェクトを`TransactionSynchronizationManager`を介して共有する方式を採用しているケースが多いようです。（少なくてもJMSとJPAは同じ方式を採用しています）

> **Note:**
>
> ちなみに・・・わたしが仕事でよく使うMyBatis(MyBatis-Spring)の`SqlSession`も、`TransactionSynchronizationManager`を介して同一トランザクション内で同じ`SqlSession`が共有されるように制御されています。


# さいごに
「まとめ」ることがなかったので、「さいごに」として・・・
「理解するシリーズ」を久々にかいてみました。今回はSpringのコア機能のひとつであるトランザクション管理の仕組みの一部を紹介しました。Springには様々な機能があるので、気が向いたら（書けるくらい自分が理解したら:grinning:）また投稿したいと思います。

By 「動いたからOK！じゃなくて・・・なぜ？どうやって？動いているのか紐解きたい人」

# 関連エントリー

以下は、トランザクション関連のエントリーです。

* [3rdパーティ製のDBアクセスライブラリをSpringのトランザクション管理下に参加させる方法](http://qiita.com/kazuki43zoo/items/d9182c8ddb2b754f150f)
* [Spring Boot 1.5からPlatformTransactionManager用のCustomizerが追加される](http://qiita.com/kazuki43zoo/items/631164864a966b5e16c5)
* [Spring Bootで@Transactionalを使わずにトランザクション制御を行う方法](http://qiita.com/kazuki43zoo/items/fc0e4145c5249c7f5f80)
* [SpringのDataSourceTransactionManagerを使うとエラー時にCommitされる可能性あり！？](http://qiita.com/kazuki43zoo/items/73225b41a2c36a720a4b)
* [MyBatis-Spring 1.3からSpringのトランザクションタイムアウト値が連携される！！](http://qiita.com/kazuki43zoo/items/1ac208a49c1d6ceeb0b8)

# 参考サイト

以下のサイトを参考にしました。

* http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#tx-decl-explained
* http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#jdbc-DataSourceTransactionManager