GroovyでMyBatisのMapperをつくる（とSQLの可読性がGood!!）

MyBatisでは、SQLやマッピング定義を記載する方法として以下の２つの方法が用意されています。

* XMLファイル（サブプロジェクトからVelocityとFreeMarkerの提供もあり）
* アノテーション（`@Select`, `@Insert`, `@Update`, `@Delete`, etc..）

よく使われる機能についてはXML及びアノテーション両方で使えますが、MyBatisの機能をフルに利用できるのはXMLファイルの方です。残念ながらアノテーションでは使えない機能があったりします（今後改善されていくことを期待しましょう！！そして、是非みなさんもPRしてみてください :wink: ）。
とはいえ、Mapperインタフェースを作るだけでSQLを実行できてしまうのは非常に魅力的で、私はちょっとしたアプリを作る（＝仕事以外の）場合はアノテーションを使います。

で・・・・アノテーションを愛用すると、ひとつの不満がでてきます :sweat_smile: それは・・・SQLの可読性がXMLファイルに比べて低いことです・・・。簡単なSQLであれば特に問題になることはありませんが、複雑なSQLを書こうとするとかなり厳しい・・・。

> **NOTE:**
>
> **2019/5/3:**
> 2019年1月22日にmybatis-spring-bootのバージョン2.0.0(2019年4月にバージョン2.0.1)がリリースされたので、内容を2.0ベースにしました。

# Groovyの複数行文字列（ヒアドキュメント！？）を使う

この不満を解消する手段として、今回はGroovyの複数行文字列（いわゆる「ヒアドキュメント」に分類される仕組み）を利用する方法を紹介します。
GroovyでMapperインタフェースを実装すると、XMLファイルと同じノリでインデントなどを設けることができちゃいます :v: 

```java:Java版(src/main/java/com/example/mybatisdemo/mapper/TodoMapper.java)
@Mapper
public interface TodoMapper {

    @Insert("INSERT INTO todo (title, details, finished) VALUES (#{title}, #{details}, #{finished})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insert(Todo todo);

    @Select("SELECT id, title, details, finished FROM todo WHERE id = #{id}")
    Todo select(int id);

}
```

```groovy:Groovy版(src/main/groovy/com/example/mybatisdemo/mapper/TodoMapper.groovy)
@Mapper
interface TodoMapper {

    @Insert('''
        INSERT INTO todo
            (title, details, finished)
        VALUES
            (#{title}, #{details}, #{finished})
    ''')
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insert(Todo todo)

    @Select('''
        SELECT
            id, title, details, finished
        FROM
            todo
        WHERE
            id = #{id}
    ''')
    Todo select(int id)

}
```

# MavenのJavaプロジェクトでGroovyを使えるようにしよう！！

Groovyを使うといい感じになりそう！！ですが、開発プロジェクトはどうすればいいの？？？ 
いろいろ方法はあるみたいですが、本投稿では[Groovy-Eclipse Maven Plugin](https://github.com/groovy/groovy-eclipse/wiki/Groovy-Eclipse-Maven-plugin)を使う方法を紹介します。
IDEとしてEclipse系を使う場合は、[Groovy-Eclipseプラグイン](https://github.com/groovy/groovy-eclipse/wiki) ＋ M2E Connectorをインストールしておくことで簡単にプロジェクトをインポートすることができます。ちなみに・・・IDEとしてIntelliJ IDEA‎を使う場合は標準でGroovyがサポートされているため、プラグインなどのインストールなどは不要です！！

なお、本投稿では以前投稿した「[mybatis-spring-boot-starterの使い方](http://qiita.com/kazuki43zoo/items/ea79e206d7c2e990e478)」で作成したプロジェクトをGroovy化する方法を紹介します。IDEへのインポート方法は説明しませんが、いまくいかなければコメントください！ベストエフォートでサポートします。

# プロジェクト（Spring Boot）を作ろう！！

「[mybatis-spring-boot-starterの使い方](http://qiita.com/kazuki43zoo/items/ea79e206d7c2e990e478)」を参照して、Java版のプロジェクトを作ってみてください。作るのが面倒な方は、以下に完成品が置いてあります。

* https://github.com/kazuki43zoo/qiita-materials/tree/master/mybatis-spring-boot/mybatis-demo

# Groovyをサポートしよう！！

まず、`pom.xml`にGroovy-Eclipse Maven Pluginの定義しましょう。

```xml:pom.xml
<dependencies>
	<!-- ... -->
	<!-- for IDE -->
	<dependency>
		<groupId>org.codehaus.groovy</groupId>
		<artifactId>groovy</artifactId>
	</dependency>
</dependencies>

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
		</plugin>
		<!-- ↓ ここから追加 -->
		<plugin>
			<artifactId>maven-compiler-plugin</artifactId>
			<configuration>
				<compilerId>groovy-eclipse-compiler</compilerId>
			</configuration>
			<dependencies>
				<dependency>
					<groupId>org.codehaus.groovy</groupId>
					<artifactId>groovy-eclipse-compiler</artifactId>
					<version>3.3.0-01</version> <!-- 投稿修正時(2019/5/3)の最新バージョン -->
				</dependency>
				<dependency>
					<groupId>org.codehaus.groovy</groupId>
					<artifactId>groovy-eclipse-batch</artifactId>
					<version>2.5.6-02</version> <!-- 投稿修正時(2019/5/3)の最新バージョン -->
				</dependency>
			</dependencies>
		</plugin>
		<!-- ↑ ここまで追加 -->
	</plugins>
</build>

```

## Groovy版の`TodoMapper.groovy`の作成

`TodoMapper.groovy`を`src/main/groovy`配下に格納します。なお、Java版の`TodoMapper.java`はこのタイミングで削除してください。

```groovy:src/main/groovy/com/example/mybatisdemo/mapper/TodoMapper.groovy
package com.example.mybatisdemo.mapper

import com.example.mybatisdemo.domain.Todo
import org.apache.ibatis.annotations.Insert
import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.Options
import org.apache.ibatis.annotations.Select

@Mapper
interface TodoMapper {

    @Insert('''
        INSERT INTO todo
            (title, details, finished)
        VALUES
            (#{title}, #{details}, #{finished})
    ''')
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insert(Todo todo)

    @Select('''
        SELECT
            id, title, details, finished
        FROM
            todo
        WHERE
            id = #{id}
    ''')
    Todo select(int id)

}
```

Groovy化した後のプロジェクト構成は、以下のような感じになります。

```text:プロジェクト構成


.
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── groovy
    │   │   └── com
    │   │       └── example
    │   │           └── mybatisdemo
    │   │               └── mapper
    │   │                   └── TodoMapper.groovy # 追加
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── mybatisdemo
    │   │               ├── MybatisDemoApplication.java
    │   │               └── domain
    │   │                   └── Todo.java
    │   └── resources
    │       ├── application.properties
    │       └── schema.sql
    └── test
        └── java
            └── com
                └── example
                    └── mybatisdemo
                        ├── MybatisDemoApplicationTests.java
                        └── mapper
                            ├── MapperTestApplication.java
                            └── TodoMapperTests.java
```

# ビルド & テスト

普通にMavenのビルドを行いテストを実行してみてください。

```text
$ ./mvnw clean test -Dtest=MybatisDemoApplicationTests
...
2019-05-03 15:21:14.709  INFO 32946 --- [           main] c.e.m.MybatisDemoApplicationTests        : Started MybatisDemoApplicationTests in 1.541 seconds (JVM running for 2.305)
ID       : 1
TITLE    : 飲み会
DETAILS  : 銀座 19:00
FINISHED : false
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.348 s - in com.example.mybatisdemo.MybatisDemoApplicationTests
2019-05-03 15:21:15.036  INFO 32946 --- [       Thread-3] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2019-05-03 15:21:15.039  INFO 32946 --- [       Thread-3] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.903 s
[INFO] Finished at: 2019-05-03T15:21:15+09:00
[INFO] Final Memory: 33M/397M
[INFO] ------------------------------------------------------------------------
```

これでGroovy化できちゃいました！！簡単ですね :laughing: 


# Lombokを使う場合は・・・

[Lombok](https://projectlombok.org/)を使う場合は、通常のdependencyの定義に加えて`maven-compiler-plugin`の設定変更が必要になります。

```xml:pom.xml
<dependencies>
	<!-- ... -->
	<!-- ↓ ここから追加 -->
	<dependency>
    		<groupId>org.projectlombok</groupId>
    		<artifactId>lombok</artifactId>
    		<scope>provided</scope>
	</dependency>
	<!-- ↑ ここまで追加 -->
</dependencies>

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
		</plugin>
		<plugin>
			<artifactId>maven-compiler-plugin</artifactId>
			<configuration>
				<compilerId>groovy-eclipse-compiler</compilerId>
				<!-- ↓ ここから追加 -->
				<compilerArguments>
					<javaAgentClass>lombok.launch.Agent</javaAgentClass>
				</compilerArguments>
				<fork>true</fork>
				<!-- ↑ ここまで追加 -->
			</configuration>
			<dependencies>
				<dependency>
					<groupId>org.codehaus.groovy</groupId>
					<artifactId>groovy-eclipse-compiler</artifactId>
					<version>3.3.0-01</version> <!-- 投稿修正時(2019/5/3)の最新バージョン -->
				</dependency>
				<dependency>
					<groupId>org.codehaus.groovy</groupId>
					<artifactId>groovy-eclipse-batch</artifactId>
					<version>2.5.6-02</version> <!-- 投稿修正時(2019/5/3)の最新バージョン -->
				</dependency>
				<!-- ↓ ここから追加 -->
				<dependency>
					<groupId>org.projectlombok</groupId>
					<artifactId>lombok</artifactId>
					<version>${lombok.version}</version>
				</dependency>
				<!-- ↑ ここまで追加 -->
			</dependencies>
		</plugin>
	</plugins>
</build>
```

# まとめ

MapperインタフェースをGroovy化することで、快適なアノテーション駆動のSQL開発ができそうです。個人的には基本はアノテーションを使い、アノテーションで実現できない機能を利用したいときだけXMLを利用すればよい気がしています。

なお、本投稿で紹介したプロジェクトの完成品は、GitHubで公開しています。

* https://github.com/kazuki43zoo/qiita-materials/tree/groovy-with-mybatis-spring-boot-2.0/mybatis-spring-boot/mybatis-demo

# 参考サイト

* http://www.ts-soft.net/archives/145
* https://github.com/groovy/groovy-eclipse/wiki/Groovy-Eclipse-Maven-plugin
* https://github.com/groovy/groovy-eclipse/wiki
* http://qiita.com/kazuki43zoo/items/ea79e206d7c2e990e478