86
89

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

MyBatisが便利だなと感じたので書いてみた

Last updated at Posted at 2018-09-24

現場で学んだことをまとめようシリーズ(はじめに)

そのまま、現場で学んだ(使用した)ツール等についてまとめていくシリーズです。
自分のためでもあり、それが誰かのためになればいいな、という気持ちを込めて書きます。
間違えや記述方法についての指摘やコメント、お待ちしております!

編集履歴

  • 2018/09/30 パラメタの渡し方を追加
  • 2019/03/27 書き方Tipsを追加

MyBatisってなんぞや?

XML、またはアノテーションを使用してSQL文とオブジェクトをマッピングするフレームワークです。

通常のCRUD操作はもちろん、
パラメータの状態により動的にSQLを発行したりできます!
何度も使い回すような記述があれば共通化して、記述量を減らしたりもできます。

本記事では主にXMLの記述について解説していきます。

基本的な記述・使い方

何はともあれソースを見ていただいた方が早いので以下をご覧ください。

Sample.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="src.mapper.MybatisMapper">

  <select id="select" resultType="src.mapper.entity.TableEntity">
    select
        *
    from 
        table_name
  </select>

  <insert id="insert">
    insert into table_name (
      id,
      name,
      age,
      department
    ) values (
      #{id},
      #{name},
      #{age},
      #{department}
    )
  </insert>

</mapper>
MyBatisMepper.java
package src.mapper

// importは省略

public interface MyBatisMapper {
  TableEntity select();

  void insert(TableEntity entity);
}
TableEntity.java
package src.mapper.entity

// importは省略

public class TableEntity {
  private String id;
  private String name;
  private Integer age;
  private String department;

  public void setId(String id) {
    this.id = id;
  }

  public String getId() {
    return this.id;
  }

  // 全フィールドのgetter、setter
}

簡単に説明しますと、

  1. <mapper namespane="src.mapper.MybatisMapper">で/src/mapper/MybatisMapper.javaと紐づけます。
  2. <select id="select" resultType="src.mapper.entity.TableEntity"> ~ </select>でMybatisMapper.select()を実行することによりselectが実行され、その結果がTableEntityにマッピングされます。
  3. パラメタを渡す場合は#{id}のように記述すれば受け取ることができます。上記ではオブジェクトで値を渡していますが、@Paramを使用すればMapperクラスの引数として受け取ることも可能です。

javaクラス、メソッドとの紐づけがとても簡単なので、直感的にわかりやすいです!

パラメタの渡し方

上記のようにデータクラスを用意できるのであれば、フィールド名でマッピングが可能です。(getterがないとダメだったような気がします)
しかし様々な都合で「マッパークラスの引数として渡したい!」という方のために、
いくつか方法をご紹介します。

XMLのパラメタを#{param1}とする

SampleParam1.xml
  <select id="select" resultType="src.mapper.entity.TableEntity">
    select
        *
    from 
        table_name
    where
        id = #{param1}
        and name = #{param2}
  </select>
MyBatisMepper.java
package src.mapper

// importは省略

public interface MyBatisMapper {
  TableEntity select(String id, String name);
}

このように記述すれば#{param1}には第一引数であるid
#{param2}には第二引数であるnameがマッピングされます!
個人的に引数の順番でマッピングされるというのは把握しにくいのでお勧めしません。

マッパークラスの引数に@Paramを付与する

SampleParam2.xml
  <select id="select" resultType="src.mapper.entity.TableEntity">
    select
        *
    from 
        table_name
    where
        id = #{id}
        and name = #{name}
  </select>
MyBatisMepper.java
package src.mapper

// importは省略

public interface MyBatisMapper {
  TableEntity select(@Param("id") String id, @Param("name") String name);
}

@Paramの引数に文字列でエイリアスを指定することにより、
XML内のパラメタとマッピングを行うことができます。
個人的には一番理解しやすくおすすめです!

mapで渡す

データクラスを作成するように、
key=パラメタ名value=値としてmapを作成する方法です。
詳しくはこちらをご覧ください。
(これやるならデータクラス作った方が分かりやすい気もしますが。。)

それでは次から動的SQLの記述方法についてご紹介いたします!

動的SQL

動的SQLというのは先述した通り、パラメタの状態によって発行されるSQLを動的に変更できることを指します!
具体的には以下の通りです!
※以後は共通する記述を省きますのでご注意ください!

if/choose/where,set,trim

例えば、参照するカラムは同じなのに条件(WHERE句)が違うから似たようなSELECT文を複数書かないといけない。。というのは億劫ですよね。
これからご紹介するif/choose/where,set,trimは、WHERE句の条件を変えたい場合やUPDATE文を統一したい場合に便利な記述方法です。

if

SampleIf.xml
  <update id="update">
    update table_name 
    set
      <if test="name != null">
        name = #{name},
      </if>
      age = #{age}
    where
      id = #{id}
  </update>
  • パラメタ.nameがnullである場合に発行されるSQL…update table_name set age = ? where id = ?
  • パラメタ.nameがnullでない場合に発行されるSQL…update table_name set name = ?, age = ? where id = ?

状況によっては二択ではなく、それ以上の条件の中から選択したい!というケースも出てくるかと思います。
そんな場合は**choose(when,otherwise)**をどうぞ。(あんまり見たことないですが)

choose(when,otherwise)

SampleChoose.xml
  <select id="select">
    select
      *
    from
      table_name
    where
      <choose>
        <when test="id != null">
          id = #{id}
        </when>
        <when test="name != null">
          name = #{name}
        </when>
        <otherwise>
          age = #{age}
        </otherwise>
      </choose>
  </select>
  • パラメタ.idがnullでない場合に発行されるSQL…select * from table_name where id = ?
  • パラメタ.idがnullであり、パラメタ.nameがnullでない場合に発行されるSQL…select * from table_name where name = ?
  • 上記のいずれにも該当しない場合に発行されるSQL…select * from table_name where age = ?

where,set,trim

where,set,trimは、少し厄介なSQLを操作したい場合に便利です。
例えば以下のようなSQLを見てみましょう。

Sample2.xml
  <select id="select2" resultType="src.mapper.entity.TableEntity">
    select
      *
    from
      table_name
    where
      <if test="id != null">
        id = #{id}
      </if>
      <if test="name != null">
        and name = #{name}
      </if>
      <if test="age != null">
        and age = #{age}
      </if>
  </update>

もしすべてのifに該当しなかった場合、select * from table_name whereというSQLが発行され構文エラーになってしまいます。
またパラメタ.nameがnullではなく、パラメタ.idとageがnullの場合はselect * from table_name where and name = ?となり、これまた構文エラーになってしまいます。

where

<where> ~ </where>を使用すると、内包するタグのいずれかが結果を返したときだけwhereを挿入してくれます。
またその内包するタグの結果がandまたはorから始まる場合にはそれらを削除してくれます!
なんて便利なんだ。。。

SampleWhere.xml
  <select id="select2" resultType="src.mapper.entity.TableEntity">
    select
      *
    from
      table_name
    <where>
      <if test="id != null">
        id = #{id}
      </if>
      <if test="name != null">
        and name = #{name}
      </if>
      <if test="age != null">
        and age = #{age}
      </if>
    </where>
  </select>

<if> ~ </if>内の要素の末尾にandorがあっても除去されないので注意してください!
 また非常に細かいですが、and(半角スペース)or(半角スペース)が除去対象となるため、タブを使用すると正常に動かない場合があります。

set

こちらはwhereのset句版です。
<set> ~ </set>を使用すれば、余計な,を削除してくれます。

SampleSet.xml
  <update id="update">
    update table_name 
    <set>
      <if test="name != null">
        name = #{name},
      </if>
      <if test="age != null">
        age = #{age},
      </if>
      <if test="department != null">
        department = #{department}
      </if>
    </set>
    where
      id = #{id}
  </update>

whereとは違い、末尾,を除去するので注意してください!

trim

上記で説明したwhereset<trim> ~ </trim>に定義することによってカスタマイズが可能です。
可読性等を考えると微妙な気もしますが、知っておいて損はないと思います!

  • <where> ~ </where>trimに置き換えた場合
SampleTrim1.xml
  <select id="select2" resultType="src.mapper.entity.TableEntity">
    select
      *
    from
      table_name
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
      <if test="id != null">
        id = #{id}
      </if>
      <if test="name != null">
        and name = #{name}
      </if>
      <if test="age != null">
        and age = #{age}
      </if>
    </trim>
  </select>
  • <set> ~ </set>trimに置き換えた場合
SampleTrim2.xml
  <update id="update">
    update table_name 
    <trim prefix="SET" suffixOverrides=",">
      <if test="name != null">
        name = #{name},
      </if>
      <if test="age != null">
        age = #{age},
      </if>
      <if test="department != null">
        department = #{department}
      </if>
    </trim>
    where
      id = #{id}
  </update>

sql,include

例えば取得用SELECT文と更新用SELECT文が必要な場合に、
書いてみたら末尾にfor updateが付くか付かないかの差分しかなかった、みたいなことはありませんか?
そういった時に<sql> ~ <sql><include> ~ </include>を使用するとすっきりします!

SampleSql.xml
  <sql id="selectBase">
    select
      *
    from
      table_name
    where
      id = #{id}
  </sql>

  <select id="select" resultType="src.mapper.entity.TableEntity">
    <include refid="selectBase" />
  </select>

  <select id="selectForUpdate" resultType="src.mapper.entity.TableEntity">
    <include refid="selectBase" />
    for update
  </select>

書き方Tips

より実践的な観点でメモをしていきます!

LIKE句を書きたい

MyBatisでは変数の書き方として二種類あります。

  • #{param}…エスケープし、シングルクォートで囲います。
  • ${param}…エスケープしません。

やりがちなのが以下のような書き方です。(idで前方一致させたい場合)

SampleSql.xml
  <select id="select3" resultType="src.mapper.entity.TableEntity">
    select
      *
    from
      table_name
    where
      id like '${id}%'
  </select>

この書き方でも正しいクエリは発行されますが、
先述した通りエスケープしてくれないのでSQLインジェクションの危険性があります。

各SQLが用意している文字列結合を使用するようにしましょう。
下記はOracleの場合です。

SampleSql.xml
  <select id="select3" resultType="src.mapper.entity.TableEntity">
    select
      *
    from
      table_name
    where
      id like #{id} || '%'
  </select>

不等号を使いたい

MyBatisはXMLで記載するので不等号<>はメタ文字として扱われてしまい、
正常にクエリを読み込むことができません。
そんな時は<![CDATA[...]]>で囲えば使用可能です!

SampleSql.xml
  <select id="select4" resultType="src.mapper.entity.TableEntity">
    <![CDATA[ 
    select
      *
    from
      table_name
    where
      create_at < #{date}
    ]]>
  </select>

ピンポイントで囲うよりもSQL全体を囲ってあげたほうが可読性は高いと思いますので、
個人的には上記のような書き方をお勧めします!

最後に

こうやってアウトプットしてみると、
いかに自分が使えているだけで理解していないかがよく分かりました。
今後も定期的に記事を投稿していこうと思いますのでまた機会がありましたらよろしくお願いいたします。
最後までお読みいただき、ありがとうございました!

参考

MyBatis公式 - 動的SQL
MyBatis公式 - XMLファイル

86
89
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
86
89

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?