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

  • 16
    Like
  • 6
    Comment
More than 1 year has passed since last update.

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

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

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

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

Groovyの複数行文字列(ヒアドキュメント!?)を使う

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

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

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

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

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

    @Insert('''
        INSERT INTO todo
            (title, details, finished)
        VALUES
            (#{title}, #{details}, #{finished})
    ''')
    @Options(useGeneratedKeys = true)
    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を使う方法を紹介します。
IDEとしてEclipse系を使う場合は、Groovy-Eclipseプラグイン + M2E Connectorをインストールしておくことで簡単にプロジェクトをインポートすることができます。ちなみに・・・IDEとしてIntelliJ IDEA‎を使う場合は標準でGroovyがサポートされているため、プラグインなどのインストールなどは不要です!!

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

プロジェクト(Spring Boot)を作ろう!!

mybatis-spring-boot-starterの使い方」を参照して、Java版のプロジェクトを作ってみてください。作るのが面倒な方は、以下に完成品が置いてあります。

Groovyをサポートしよう!!

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

pom.xml
<dependencies>
    <!-- ... -->
    <!-- for IDE -->
    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-all</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>2.9.2-01</version> <!-- 投稿時(2016/9/22)の最新バージョン -->
                </dependency>
                <dependency>
                    <groupId>org.codehaus.groovy</groupId>
                    <artifactId>groovy-eclipse-batch</artifactId>
                    <version>2.4.3-01</version> <!-- 投稿時(2016/9/22)の最新バージョン -->
                </dependency>
            </dependencies>
        </plugin>
        <!-- ↑ ここまで追加 -->
    </plugins>
</build>

Groovy版のTodoMapper.groovyの作成

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

src/main/groovy/com/example/mapper/TodoMapper.groovy
package com.example.mapper

import com.example.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)
    void insert(Todo todo);

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

}

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

プロジェクト構成
.
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── groovy
    │   │   └── com
    │   │       └── example
    │   │           └── mapper
    │   │               └── TodoMapper.groovy # 追加
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           ├── MybatisDemoApplication.java
    │   │           └── domain
    │   │               └── Todo.java
    │   └── resources
    │       ├── application.properties
    │       └── schema.sql
    └── test
        └── java
            └── com
                └── example
                    └── MybatisDemoApplicationTests.java

ビルド & テスト

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

$ ./mvnw clean test
...
ID       : 1
TITLE    : 飲み会
DETAILS  : 銀座 19:00
FINISHED : false
2016-09-22 21:40:44.936  INFO 99479 --- [           main] com.example.MybatisDemoApplicationTests  : Started MybatisDemoApplicationTests in 2.117 seconds (JVM running for 2.801)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.207 sec - in com.example.MybatisDemoApplicationTests
2016-09-22 21:40:44.965  INFO 99479 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3098cf3b: startup date [Thu Sep 22 21:40:43 JST 2016]; root of context hierarchy

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.995 s
[INFO] Finished at: 2016-09-22T21:40:45+09:00
[INFO] Final Memory: 19M/307M
[INFO] ------------------------------------------------------------------------

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

Lombokを使う場合は・・・

Lombokを使う場合は、通常のdependencyの定義に加えてmaven-compiler-pluginの設定変更が必要になります。

pom.xml
<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>2.9.2-01</version>
                </dependency>
                <dependency>
                    <groupId>org.codehaus.groovy</groupId>
                    <artifactId>groovy-eclipse-batch</artifactId>
                    <version>2.4.3-01</version>
                </dependency>
                <!-- ↓ ここから追加 -->
                <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>1.16.10</version> <!-- 投稿時(2016/9/22)の最新バージョン -->
                </dependency>
                <!-- ↑ ここまで追加 -->
            </dependencies>
        </plugin>
    </plugins>
</build>

まとめ

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

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

参考サイト