出会い
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.java
にexports xxx;
を追加すればいいだけだけど、すぐ分からなかった。。
-