MyBatisが色々てんこ盛りになっていい感じになっている件

久しぶりにSpringBootの環境構築をやろうかと思い、ORMをどうするか選定していたところ、MyBatisが結構進化していたので、メモ。

MyBatis3DynamicSql

  • XMLが不要になる
  • コード量が小さくなる
  • JavaでタイプセーフにSQLがかける(JOOQみたい)
  • https://mybatis.org/generator/quickstart.html

こんな感じでかけます。

        List<TableCode> records = mapper.selectByExample()
                .where(id, isEqualTo(3))
                .build()
                .execute();       

        List<TableCode> records = mapper.selectByExample()
                .where(id, isEqualTo(3))
                .or(description, isLike("f%"))
                .build()
                .execute();    

        List<TableCode> records = mapper.selectByExample()
                .where(id, isLessThan(10), and(description, isEqualTo("foo")))
                .or(description, isLike("b%"))
                .orderBy(id.descending())
                .build()
                .execute(); 

JOOQみたいに、タイプセーフでSQLがかけるのはとてもすごい進化だと思います。

FreeMarker Tempalte

前に書いたとおりのFreeMarker Templateは健在。XMLがいやでFreeMarkerにしたけど、複雑なクエリはやはりJavaコードじゃなくて、素のSQLの方がいいイメージなので、継続して使用します。

sample.ftl

select
 *
from
  sample_table
where
  id = <@p name="param.id" />

こんな感じでかける。

from句とかを別のftlファイルにして、includeして使うことも可能です。

Thymeleaf Template

Doma2とかでやっている2WaySQLがThymeleaf Templateで実現している。Doma2より書き方は野暮ったいけど、一応2waySQLはできる。

SELECT * FROM names
  WHERE 1 = 1
  /*[# th:if="${not #lists.isEmpty(ids)}"]*/
    AND id IN (/*[# mb:p="ids"]*/ 1 /*[/]*/)
  /*[/]*/
  ORDER BY id

MyBatis Migrate

これは前からあったみたいなんだが、Flywayの陰に隠れて知らなかった。
機能的には十分っぽいので、試してみる。

詳細 => https://mybatis.org/migrations/

Mybatis Log Plugin (Intellijのみ)

これがあると、条件分岐があって、引数のバインドが全部終わったSQLを出力してくれるので、デバッグに便利です。
必需品かと思います。(Domaは標準でありますが)

https://plugins.jetbrains.com/plugin/10065-mybatis-log-plugin

最後に

タイプセーフでSQLかけるし、2WaySQLあるし、Migrateあるし、なんかMyBatisで全部OKなんじゃないかとおもいました。おそらくThymeleafは

mybatisでカスタムクエリをxmlで書くのは辛いので、freemarker templateにする

mybatisでカスタムでクエリ書くときに、標準だとxml形式で書かなきゃいけなくて、結構しんどい。

カンタンに書くと下のような感じ

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="jp.sample.MapperCustom">
  <delete id="deleteAll" parameterType="map">
    delete from ${table}
  </delete>

  <insert id="bulkInsert" parameterType="map">
    insert into ${table}
    (code)
    values
    <foreach item="v" collection="list" separator=",">
      (#{v})
    </foreach>
  </insert>
</mapper> 

つらいポイント
– xml形式なので、余計なものが先頭に来てつらい
– 繰り返しがつらい

なんかたくさんあるかと思いきや、あんまり挙げられなかったのだが、とにかくXMLというのが辛い。不等号もxml形式なので、CDATAを利用してかかないければならないのとか、そうしないなら、エスケープしないといけないのが辛かった。

Groovyとかkotlinだとmulti line strings が使用できる話だが、Groovyだと大きいJarを入れないとだし、kotlinだとnull値が。。。みたいな問題に悩まされるんじゃないかという気がしたので、mybatis freemarker templateを使う方法を選択した。

使い方

ライブラリの追加

gradleのdependenciesに下記を追加

compile("org.mybatis.scripting:mybatis-freemarker:$mybatisFreemakerVersion")

freemarkerテンプレートを置き場指定

src/main/resources 直下にmybatis-freemarker.propertiesのファイルを設置する
内容は、mybatis-freemarkerを使う際にどのディレクトリを参照するかなので、classpath内の任意のディレクトリパスを記述する。

basePackage=mybatis/sql

freemarker templateにクエリを書く

src/main/resources/mybatis/sql/sample.ftl

select
  name
from
  sample
where
  id in (${ids?join(", ")})
  and
  name = <@p name="n">

(${ids?join(", ")})とか<@p name="n"> とかのfreemarker templateでかけるのは嬉しい。xml形式でない分可読性も高くなると思うし、使い勝手がよくなる。

mapperインターフェース内にfreemarker templateの呼び出しを書く

@Lang(FreeMarkerLanguageDriver.class)
@Select("sample.ftl")
List<Name> findNamesByIds(@Param("n") String name,@Param("ids") List<Integer> ids);

以上のように書けば、実行できる。なんかかなりスッキリしたよい感じになる。デメリットとしては、1SQLで1ファイル使うので結構ファイルの数は増える。ディレクトリ等を使えば、スッキリはすると思うので、大したデメリットにはならないと思う。

xmlを敬遠しがちだったが、こっちならバリバリSQLをかけるような気がする