Spring4勉強会 第五回

# Spring4勉強会 第五回

前回、MyBatisと連携し、一通りのDMLは体感しました。
今回は「**Transaction制御**」の紹介です。

* 環境構築
* 動かしてみる
* @Transactionalについて

早速

# 環境構築

トランザクション制御をMySQLで叶えるために、二つの設定をして頂きます。

1. **auto_commitのOFF**
2. **@Transactionalの有効化**

どちらの作業も「**mvc-config.xml**」に追記するだけです。

以下、適用後のxmlファイルになります。

```
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

	<context:component-scan base-package="jp.co.kenshu" />
	<mvc:annotation-driven />

    <!-- 入力値検証の日本語リソース設定 -->
	<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
	   <property name="basename" value="classpath:ValidatorMessages" />
	</bean>


    <!-- ControllerからViewをforwardする際にどこのViewを検索するかの設定 -->
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' -->
		<property name="prefix" value="/WEB-INF/view/" />
		<property name="suffix" value=".jsp" />
	</bean>


	<!-- DBの設定 -->
	<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost/test" />
		<property name="username" value="xxx" />
		<property name="password" value="xxx" />
+		<!-- デフォルトでautoコミットがONなのでOFFにする -->
+		<property name="defaultAutoCommit" value="false" />
	</bean>

	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="jp.co.kenshu.mapper" />
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	</bean>

    <!-- どこのパッケージをmapperとして認識するかの設定 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="jp.co.kenshu.mapper" />
	</bean>

	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
+    <!-- @Transactionalを使用するために定義 -->
+	<tx:annotation-driven />

</beans>
```

「+」が付いている箇所が今回追加した設定箇所です。


```
+		<!-- デフォルトでautoコミットがONなのでOFFにする -->
+		<property name="defaultAutoCommit" value="false" />
```

コメントに記述した通りなのですが、今回はMySQLを使用しています。
MySQLはデフォルトでAUTO_COMMITがONになっているので、JDBCコネクション時にOFFにします。
これをしないと、トランザクションが開始しません。

```
+    <!-- @Transactionalを使用するために定義 -->
+	<tx:annotation-driven />
```

この記述のおかげで、今回やろうとしている「**アノテーションだけでTransaction制御を行う**」が実現可能になります。
これがないと、**アノテーションは記述できるけど、機能しない**事態に陥ります。

環境構築は以上です。

# 動かしてみる

準備ができたので、動作確認用のController、Service、Dao等を記述して参りましょう！！

今回は動作確認するために、以下のような処理を記述します。

1. GETパラメータで指定されたIDを受取、delete処理を行う（正常削除）
2. 続いてinsert処理を行うが、存在しないテーブルに対してinesrtする(insert失敗・かつエラー画面表示)
3. 1の削除処理がrollbackされる
4. [http://localhost:8080/SpringKenshu/test/](http://localhost:8080/SpringKenshu/test/)にアクセスし、1で消したはずのIDが生き残っていることを確認する。

▼画面イメージ

![tran.png](https://qiita-image-store.s3.amazonaws.com/0/78857/bc5f13a8-30bb-7f04-dd95-6fd14c73e5f2.png)
***

まずはID27が存在することを確認
続いて[http://localhost:8080/SpringKenshu/test/transaction/27](http://localhost:8080/SpringKenshu/test/transaction/27)にアクセスし、
IDが27のtestを削除しようとします。

![inserterror.png](https://qiita-image-store.s3.amazonaws.com/0/78857/680333d1-b4c8-89b2-1649-aa130d28cda7.png)
***

しかし、deleteが成功しますが、insert時に「**testestなんてテーブルは存在しない**」というエラーでこけます。
そして、その後[http://localhost:8080/SpringKenshu/test/](http://localhost:8080/SpringKenshu/test/)にアクセスし、1で消したはずのIDが生き残っていることを確認します。

![tran.png](https://qiita-image-store.s3.amazonaws.com/0/78857/b46e0b65-f34f-6825-902d-4fdca931bdb4.png)
***

早速実装してみましょう。

## Controllerの実装

TestControlerに以下を追加

```
// トランザクションのサンプル
@RequestMapping(value = "/test/transaction/{id}", method = RequestMethod.GET)
public String testTransaction(Model model, @PathVariable int id) {
	TestDto dto = new TestDto();
	dto.setId(id);
	testService.deleteAllAndInsert(dto);
	return "redirect:/test/";
}
```

続いてService

TestServiceに以下を追加

```
// transactionのテスト
@Transactional
public void deleteAllAndInsert(TestDto dto) {
	int delCount = testMapper.deleteTest(dto.getId());
	Logger.getLogger(TestService.class).log(Level.INFO, "削除件数は" + delCount + "件です。");
	// ここのinsertは失敗する。deleteがrollBackされるかどうか。
	int insCount = testMapper.insertFailTest(null);
	Logger.getLogger(TestService.class).log(Level.INFO, "挿入件数は" + insCount + "件です。");
}
```

Serviceはdeleteを行ってから、insertを行ってます。
delete成功時にLogを吐き出しているので、ここで成功しているかも確認できます。

```
@Transactional
```

このアノテーションがあるメソッドはトランザクション管理が発生します。
アノテーションはクラス単位でも付与できるため、クラス全体でトランザクション制御も設定可能です。

続いて、失敗するためのDaoを書きます。
TestMapper.javaに以下を追記。

```
// トランザクションテスト用の失敗メソッド
int insertFailTest(Object object);
```

TestMapper.xmlにも以下を追記

```
<!-- トランザクション失敗用insert -->
<insert id="insertFailTest" parameterType="String">
    insert into testtest(name)
    values (#{name})
</insert>
```

これを見ると、意図的に

```
insert into testtest
```

と、「testest」と存在しないテーブルを指定しています。
これで毎回必ずinsertに失敗します！！

準備は以上です。
手順通りに実際に動かしてみてください。

deleteは成功しているのに、insertでこけるので、@Transactionalが働き、ID27が削除されていないことがわかるかと思います。

Serviceのこの記述が全てですね。

```
@Transactional
public void deleteAllAndInsert(TestDto dto) {
```

たったアノテーションの記述一行でトランザクション処理が実現できました。

# @Transactionalについて

@Transactionalを記述しただけで、トランザクション制御が実現可能になりました。
クラス単位でも、メソッド単位でも指定できます。

細かい挙動の設定をするにはリファレンスを参照してください。
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html

今回は以上です。