LoginSignup
1
2

More than 5 years have passed since last update.

SIerに入って8年ぐらい経ったのでいろいろ棚卸しをしてみる。【vol.003 MyBatis】

Last updated at Posted at 2019-04-14

出会い

vol.002に書いた通り、自分にとっては初のコーディングPJで出会った。
自分が出会った当時は、iBATISと呼ばれていて後に後継としてMyBatisになっている。

MyBatisとは

MyBatis(マイバティス)とは、iBATIS(アイバティス)の後継プロジェクトで、マッピングファイルにSQL文を直接記述し「オブジェクトとSQL実行結果との間」でマッピングを行うという特徴を持つ、Javaおよび.NET Frameworkを対象とするORマッピングライブラリです。オープンソースのフレームワーク/MyBatisとは

  • JDBCだとSQLをJavaのクラスに書くことになるため、その点、密結合な状態だった。
  • また、ResultSetから取得結果を取り出して、クラスにマッピングする分、手間があった。
  • MyBatisだとSQLを外部xmlファイルに記述することで、クラスとSQLを疎結合な状態にできる。
  • また、クラスへのマッピングはルールに基づいて記述すれば、自動マッピングしてくれる。
  • つまり、JDBCに比べるとMyBatisはちょっと気の利くやつである。

振り返ってみて

2012年頃の経験に基づくが、SQLを外部xmlファイルに記述しているメリットもあったが、デメリットもあったように思う。

【メリット】
使用するSQLが特定のxmlにまとまっているため、SQLのみを一括修正したりするときに楽だった。
当時の現場では、機能単位ごとにxmlを分割していたので、調査・修正の際にSQLにたどり着くのは早かった気がする。

【デメリット】
動的にSQLを生成しないので検索パターンごとにSelectのクエリーを用意することになっていた。
検索条件のパターンが多いものほど不毛さを感じた。※現在、MyBatisでは動的SQLをサポートしている。
当時のPJの先輩が「設計書から自動でSQLの外部ファイル生成」するExcelマクロを用意していたのを思い出した。

静的にSQLをxmlに定義しておいて、それを利用するのがMyBatisの良い使い方だと思う。
「動的にSQLを生成する」ことと「静的にSQLを定義しておく」ことを天秤にかけて、機能要件次第で採用判断になるのかなと思う。

経験上、Webアプリの検索機能でよっぽどシンプルな検索条件でもない限り、多数の検索クエリーに対応することになる。パターンごとにSQLを静的に用意しておくと、SQLが爆裂するのでどうしても動的にSQLを生成することを検討せざるをえない。

個人的には、SQLが外部ファイル化されているメリットってそんなに感じないので、動的SQLに対応していたとしてもMyBatisは使わない気がする。ただ、よっぽど大きなPJで「SQLだけ書くDB寄りのチーム」と「Javaでコーディングするチーム」で分けた方が生産性が高くなるなら採用するのかもしれない。

サンプルで実装してみる。

以下を参考にさせていただきました。

package sample.mybatis.java.dao;

import java.io.IOException;
import java.sql.SQLException;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import sample.mybatis.java.dao.mapper.WorkerMapper;
import sample.mybatis.java.model.Worker;

public class SampleDao {

    public static SampleDao instance = new SampleDao();

    private SampleDao () {

    }

    public List<Worker> select() throws SQLException, IOException {

        List<Worker> workerList = null;

        try (var in = Resources.getResourceAsStream("mybatis-config.xml")) {
            var factory = new SqlSessionFactoryBuilder().build(in);

            try (var session = factory.openSession()) {
                var mapper = session.getMapper(WorkerMapper.class);
                workerList = mapper.selectAll();
            }
        }

        return workerList;
    }

    public void insert() throws SQLException, IOException {

        try (var in = Resources.getResourceAsStream("mybatis-config.xml")) {
            var factory = new SqlSessionFactoryBuilder().build(in);

            try (var session = factory.openSession()) {
                var mapper = session.getMapper(WorkerMapper.class);
                var worker = new Worker("0001", "k.jarrett", 73, "music");
                mapper.insert(worker);
                session.commit();
            }
        }
    }

    public void update() throws SQLException, IOException {

        try (var in = Resources.getResourceAsStream("mybatis-config.xml")) {
            var factory = new SqlSessionFactoryBuilder().build(in);

            try (var session = factory.openSession()) {
                var mapper = session.getMapper(WorkerMapper.class);
                var worker = new Worker("0001", "k.jarrett", 74, "music");
                mapper.update(worker);
                session.commit();
            }
        }
    }

    public void delete() throws SQLException, IOException {

        try (var in = Resources.getResourceAsStream("mybatis-config.xml")) {
            var factory = new SqlSessionFactoryBuilder().build(in);

            try (var session = factory.openSession()) {
                var mapper = session.getMapper(WorkerMapper.class);
                var worker = new Worker("0001", "k.jarrett", 74, "music");
                mapper.delete(worker);
                session.commit();
            }
        }
    }
}

<?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="sample.mybatis.java.dao.mapper.WorkerMapper">
  <resultMap id="workerResultMap" type="sample.mybatis.java.model.Worker">
    <result property="id" column="id" />
    <result property="name" column="name" />
    <result property="age" column="age" />
    <result property="department" column="department" />
  </resultMap>
  <select id="selectAll" resultMap="workerResultMap">
    select * from worker
  </select>
  <insert id="insert" parameterType="sample.mybatis.java.model.Worker">
    insert into worker
    values (
      #{id},
      #{name},
      #{age},
      #{department}
    )
  </insert>
  <update id="update" parameterType="sample.mybatis.java.model.Worker">
    update worker
    set name = #{name},
        age = #{age},
        department = #{department}
    where id =  #{id}
  </update>
  <delete id="delete" parameterType="sample.mybatis.java.model.Worker">
    delete from worker
    where id = #{id}
  </delete>
</mapper>
package sample.mybatis.java.dao.mapper;

import java.util.List;

import sample.mybatis.java.model.Worker;

public interface WorkerMapper {

    List<Worker> selectAll();
    void insert(Worker worker);
    void update(Worker worker);
    void delete(Worker worker);
}

サンプルプロジェクトごとGitHubにプッシュしています。
https://github.com/TsJazz27Sumin/mybatisSample/tree/master/src

サンプルで実装してみて感想。

  • Javaのクラスとxmlを行ったり来たりするのがめんどくさい。
  • 一度、設定すれば変更の手間はそんなにかからないのだろうけど、やっぱり設定多い。
  • だいぶアノテーションを使って楽にできるように進化しているみたいだけど、これならJPAで良い気がする。(※MyBatis Mapper アノテーションの使い方
  • リフレクションのエラー:java.lang.reflect.InaccessibleObjectException: Unable to make ~にハマッた。
    • module-info.javaexports xxx;を追加すればいいだけだけど、すぐ分からなかった。。
1
2
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
1
2