MirageSQLとSpringの連携（続・MirageSQL超入門）

# はじめに
以前、MirageSQL超入門という記事を書いてから、ずいぶん時間がたってしまいました。
後日掲載と予告だけして、予告詐欺でしたね・・・
さて、気を取り直して本題です。
今回のテーマは、MirageSQLとSpringの連携です。
Mirageって何？と思われた方はこちら、[【超初心者向け】MirageSQL超入門](https://qiita.com/tarosa0001/items/8b500ff605fe9246bd28)をご覧ください。

テーマに掲げた内容をサンプルプロジェクトとして公開するだけなので、日本語の説明は少なめで行きます。

# プロジェクト準備
準備をしましょう。
今回もEclipse + Java8を使用します。
ライブラリは以下に一覧にしますが、今回はSpring5を使ってみました。
ちょっと前って、Spring5系のjarって手に入らなったと思うんですけど、最近はMavenリポジトリに公開されてるんですね。
うれしくなったので、必要ライブラリは自力で集めました。

## 使用ライブラリ一覧
|ライブラリ名|バージョン|
|:---------|:-------|
|commons-dbcp2|2.9.0|
|commons-pool2|
|javassist|3.12.1.GA|
|miragesql|2.1.2|
|miragesql-integration|2.1.2|
|ognl|3.1.21|
|ojdbc|8|
|spring-aop|5.0.9.RELEASE|
|spring-beans|5.0.9.RELEASE|
|spring-context|5.0.9.RELEASE|
|spring-core|5.0.9.RELEASE|
|spring-expression|5.0.9.RELEASE|
|spring-jcl|5.0.9.RELEASE|
|spring-jdbc|5.0.9.RELEASE|
|spring-tx|5.0.9.RELEASE|

## プロジェクトツリー
```
プロジェクトルート
├─lib
│      commons-dbcp2-2.9.0.jar
│      commons-pool2-2.9.0.jar
│      javassist-3.12.1.GA.jar
│      miragesql-2.1.2.jar
│      miragesql-integration-2.1.2.jar
│      ognl-3.1.21.jar
│      ojdbc8.jar
│      spring-aop-5.0.9.RELEASE.jar
│      spring-beans-5.0.9.RELEASE.jar
│      spring-context-5.0.9.RELEASE.jar
│      spring-core-5.0.9.RELEASE.jar
│      spring-expression-5.0.9.RELEASE.jar
│      spring-jcl-5.0.9.RELEASE.jar
│      spring-jdbc-5.0.9.RELEASE.jar
│      spring-tx-5.0.9.RELEASE.jar
│
├─resources
│  ├─properties
│  │      jdbc.properties
│  │
│  ├─spring
│  │      ApplicationContext.xml
│  │
│  └─sql
│          SelectUserTable.sql
│          UpdateUserTable.sql
│
└─src
    └─jp
        └─co
            └─tarosa
                ├─bean
                │      ExecuteSQL.java
                │
                ├─entity
                │      UserTable.java
                │
                └─main
                        SpringMirageSQL.java

```
# 実装
ここから実装です。
Javaと設定回りの順で紹介します。

## Java
```java:Main処理
package jp.co.tarosa.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * <h3>[SpringとMirageSQLの連携]</h3><br>
 * @author Tarosa001
 */
public class SpringMirageSQL {
	/**
	 * <h3>[Main処理]</h3><br>
	 * Springを読み込み、Beanを起動する。
	 * @param args コマンド引数
	 */
	public static void main(String[] args) {
		try (ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("spring/ApplicationContext")) {
			// Contextから読み込んだBeanを実行する
            appContext.getBean("実行業務");
		} catch (Exception e) {
			System.err.println("エラーです。ロールバックしてるはずです。");
		}
	}
}
```

```java:テーブルのエンティティ
package jp.co.tarosa.entity;

import com.miragesql.miragesql.annotation.Column;
import com.miragesql.miragesql.annotation.PrimaryKey;
import com.miragesql.miragesql.annotation.PrimaryKey.GenerationType;

/**
 * <h3>[USER_TABLEエンティティ]</h3><br>
 * @author Tarosa001
 */
public class UserTable {
	/** ユーザーID */
	@PrimaryKey(generationType = GenerationType.APPLICATION)
	@Column(name = "USER_ID")
	private String userId;

	/** ユーザー名 */
	@Column(name = "USER_NAME")
	private String userName;

	/**
	 * <h3>[ユーザーID取得処理]</h3><br>
	 * @return ユーザーID
	 */
	public String getUserId() {
		return userId;
	}

	/**
	 * <h3>[ユーザーID設定処理]</h3><br>
	 * @param userId ユーザーID
	 */
	public void setUserId(String userId) {
		this.userId = userId;
	}

	/**
	 * <h3>[ユーザー名取得処理]</h3><br>
	 * @return ユーザー名
	 */
	public String getUserName() {
		return userName;
	}

	/**
	 * <h3>[ユーザー名]</h3><br>
	 * @param userName ユーザー名
	 */
	public void setUserName(String userName) {
		this.userName = userName;
	}
}
```

```java:SQLを実行するBean
package jp.co.tarosa.bean;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.transaction.annotation.Transactional;

import com.miragesql.miragesql.ClasspathSqlResource;
import com.miragesql.miragesql.SqlManager;
import com.miragesql.miragesql.SqlResource;

import jp.co.tarosa.entity.UserTable;

/**
 * <h3>[SQL実行]</h3><br>
 * SQLを実行する処理を持つクラス。<br>
 * クラスごとTransactionalを付与することもできるが、今回はメソッドごとに付与する。
 * @author Trasosa0001
 */
public class ExecuteSQL {
	/** SQLマネージャー */
	private SqlManager manager;

	/**
	 * <h3>[検索処理]</h3><br>
	 * USER_TABLEからパラメーターで指定した条件のレコードを検索して返す。<br>
	 * 途中の処理で例外が発生した場合、ロールバックする。<br>
	 * SELECTのSQLではロールバックの必要はないが、Springのコントロール下に置く場合、
	 * Transactionalの付加が必須になる。
	 * @return 検索結果
	 */
	@Transactional(rollbackForClassName={"Exception"})
	public List<UserTable> select() {
		// --------------------------------------------------------------------
		// パラメータの設定(in句に設定してみる)
		// --------------------------------------------------------------------
		List<String> userIdList = new ArrayList<>();
		userIdList.add("1");
		userIdList.add("2");

		// --------------------------------------------------------------------
		// 実際に引き渡すパラメーター
		// --------------------------------------------------------------------
		Map<String, List<String>> params = new HashMap<>();
		params.put("USER_ID_LIST", userIdList);

		// --------------------------------------------------------------------
		// SQL実行
		// --------------------------------------------------------------------
		SqlResource sql = new ClasspathSqlResource("sql/SelectUserTable.sql");
		List<UserTable> li = manager.getResultList(UserTable.class, sql, params);

		li.forEach(l -> System.out.println(l));

		return li;
	}

	/**
	 * <h3>[更新処理]</h3><br>
	 * パラメーターで指定した条件のレコードを更新する。<br>
	 * 検索処理同様、処理中に例外が発生した場合、ロールバックする。<br>
	 * SpringのTransactionalでトランザクション管理を行う場合は、
	 * Transactionalを付与したメソッドが2つの条件に当てはまっている必要がある。<br>
	 *   1. Spring管理下のBean内のメソッドであること<br>
	 *   2. メソッドのスコープがpublicであること<br>
	 * この2つに当てはまらないと、コミットもロールバックもされない。<br>
	 * また、付与するアノテーションはMirageではなく、必ずSpringのものを使用する。
	 */
	@Transactional(rollbackForClassName={"Exception"})
	public void update() {
		// --------------------------------------------------------------------
		// パラメーター
		// --------------------------------------------------------------------
		Map<String, String> params = new HashMap<>();
		params.put("USER_NAME", "太郎");
		params.put("USER_ID", "3");

		// --------------------------------------------------------------------
		// SQL実行
		// --------------------------------------------------------------------
		SqlResource sql = new ClasspathSqlResource("sql/UpdatetUserTable.sql");
		manager.executeUpdate(sql, params);
	}

	/**
	 * <h3>[SQLマネージャー設定処理]</h3><br>
	 * @param manager SQLマネージャー
	 */
	public void setManager(SqlManager manager) {
		this.manager = manager;
	}
}
```

## 設定
```properties:jdbc.properties
# Spring連携する場合は必要ないのですが
jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:ORCL
jdbc.user=SCOTT
jdbc.password=TIGER
```

```xml:ApplicationContext.xml
<?xml version="1.0" encodling="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.spring.framework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework/orc/schema/p"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

	<!-- DB設定の読み込み -->
	<context:property-placeholder
			location="classpath:/properties/jdbc.properties"
			order="1" ignore-unresolvable="true" />

	<!-- Bean -->
	<bean id="bean" class="jp.co.tarosa.bean.ExecuteSQL">
		<property name="manager" ref="sqlManager" />
	</bean>

	<!-- @Transactionalを使用可能に設定 -->
	<tx:annotation-driven />

	<!-- DB接続設定 -->
	<!-- ↓↓↓↓↓ ここから ↓↓↓↓ -->
	<!--
		Spring連携では、DB接続定義ここに書くことができます。
		泣き所？だった、接続先が1つしか定義できない問題も解決できます。
	-->
	<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
		<property name="driverClassName"   value="${jdbc.driver}" />
		<property name="url"               value="${jdbc.url}" />
		<property name="username"          value="${jdbc.user}" />
		<property name="password"          value="${jdbc.password}" />
		<property name="defaultAutoCommit" value="false" />
	</bean>

	<!--
		トランザクションマネージャーを設定します。
		ここでは、Mirageのものではなく、必ずSpringのクラスを使用しましょう。
	-->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<!--
		コネクションプロバイダーを設定します。
		ここではMirageのものを使用します。
		ただし、こちらは新しめのMirageを使用すると、
		パッケージがmiragesql-integration-2.x.x.jarに分かれているので、注意が必要です。
	-->
	<bean id="connectionProvider" class="com.miragesql.miragesql.integration.spring.SpringConnectionProvider">
		<property name="transactionManager" ref="transactionManager" />
	</bean>

	<bean id="dialect" class="com.miragesql.miragesql.dialect.OracleDialect" />

	<bean id="sqlManager" class="com.miragesql.miragesql.SqlManagerImple">
		<property name="connectionProvider" ref="connectionProvider" />
		<property name="dialect" ref="dialect" />
	</bean>
	<!-- ↑↑↑↑↑ ここまで ↑↑↑↑ -->
</beans>
```

```sql:SelectUserTable.sql
select
	*
from
	USER_TABLE
where
    -- どさくさに紛れてIN句を使うときのサンプルです
	USER_ID in /*USER_ID_LIST*/(0)
```

```sql:UpdateUserTable.sql
update
	USER_TABLE
set
	USER_NAME = /*USER_NAME*/'T'
where
	USER_ID = /*USER_ID*/0
```

# まとめ
ロクに本文もなくまとめですが、ポイントはソースコード中コメントに埋め込んでいますので、詳しくはそちらをご覧ください。

基本はSpringの設定になるので、参考はほかにもたくさんあると思います。
あとはMirageの使用感にあわせるだけですが、パッケージされているjarが分割されていたり、jarの中のパッケージ構成が古いものからガラっと変わっていたりするので、そこを注意しましょう。
ということと、コネクションプロバイダーやアノテーションなどSpringのクラスを使うところとMirageのクラスを使うところが混じり気味なので、そこにも気を付けましょう。