JavaのオススメSQLフレームワーク「MyBatis」

皆さんはプログラムでDaoからデータベースにアクセスする際、何をお使いでしょうか。

今回、ご紹介するのはJava言語用のDBアクセス用フレームワークMyBatisです。ご存じの方も多いと思います。iBatisの後継と言われればピンと来る方もいるかもしれません。現在はiBatisの開発が中止され、MyBatisとしてプロジェクトがフォークされています。

■MyBatis公式サイト
http://www.mybatis.org/mybatis-3/ja/index.html

なぜ、MyBatisなのか。

個人的には、JPQLなどの記述はあまり好きではありません。
JPQLによる、プログラムを組み上げていくような記述はプログラマー以外には非常に読みにくいものです。設計書のSQLをプログラムに落とし込むのも手間がかかりますしね。
何十行、何百行にもわたるようなSQLだと、もううんざり。

例:

Query q = em.createQuery(
                   "SELECT id, name  " +
                   "FROM Customer c " +
                   " WHERE c.name LIKE "Smith");

最近ではMyBatisと同様な感じで外部ファイルに記述できるようにもなったみたいです。ただし、実装者が望めば、これまでどおりの記述もできるわけです。これもまた混乱を招きます。個人的には設計書とプログラムが剥離していく原因にもなりかねないと思っています。要はメンテが大変なわけです。

一方、MyBatisはSQL部分をそもそもXMLのリソースファイル上に記述して扱います。実装方法に統一性が図れますし、設計書からプログラムへの実装も基本はコピペで済みます
(XML特殊文字などはCDATAタグで括る必要はありますが)。ifなどのタグを使えばJPQL同様、動的なSQLも扱えますしね。

ただし、問題は共通的なSQL、すなわちselece by primarykey, update, insertなどのSQLも記述が必要になる点です。

機械的な作業に時間を取られるとコストに跳ね返りますし、精神的にも良くありません(←これ重要)。

そこでApache velocityなどを使った自動生成ツールが流行りました。このようなツールは市場に出回っているので、お金にモノを言わせば簡単に実装できます。

でも、私はお金にそんなに恵まれていません。iPhoneだって未だに”5S”です。質素な私にピッタリなツール、それがMyBatis generatorです。MyBatis公式の自動生成ツールとなります。

実行時にデータベースからテーブルの情報を取得し、共通的なSQLやアクセスクラスを自動生成してくれます。

■MyBatis Generator
http://www.mybatis.org/generator/

インストールするにはGithubにアクセスし、Zipをダウンロードしてきます。
https://github.com/mybatis/generator.git

Eclipseのプラグインに上記のファイルを組み込み、
Eclipseでプロジェクトを作成し、プロジェク直下にAnt実行用のbuild.xmlとgeneratorConfig.xmlという設定ファイルを作成し、Ant実行すると出来上がります。

build.xmlサンプル

<?xml version="1.0" encoding="UTF-8"?>
<project default="genfiles" basedir=".">
 <property name="generated.source.dir" value="${basedir}" />

 <target name="genfiles" description="Generate the files">
   <taskdef name="mbgenerator" classname="org.mybatis.generator.ant.GeneratorAntTask"
   	classpath="C:\Program Files\eclipse\plugins\org.mybatis.generator.core_1.3.2.201207161521.jar" />
   <mbgenerator overwrite="true" configfile="generatorConfig.xml" verbose="false">
     <propertyset>
       <propertyref name="generated.source.dir" />
     </propertyset>
   </mbgenerator>
 </target>
</project>

generatorConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration >
	<classPathEntry location="D:\Documents\tool\Oracle\instantclient_11_2\ojdbc6.jar" />
  <context id="context1" >
    <jdbcConnection driverClass="oracle.jdbc.driver.OracleDriver" connectionURL="jdbc:oracle:thin:@xxx.xxx.xxx.xxx:1521:ORCL" userId="xxx" password="********" />
    <javaModelGenerator targetPackage="com.common.dao.model" targetProject="D:\Documents\java\test2\test\src\main\java" />
    <sqlMapGenerator targetPackage="com.common.dao.sqlmap" targetProject="D:\Documents\java\test2\test\src\main\resources" />
    <javaClientGenerator targetPackage="com.common.dao" targetProject="D:\Documents\java\test2\test\src\main\java" type="XMLMAPPER" />
    <table tableName="Customer" />
    <table tableName="ContractInfo" />
...
  </context>
</generatorConfiguration>

このように自動生成させたい対象のテーブルを記述していくだけです。
ちなみに自動生成されたものをJavaで利用するとこんな感じになります。

public static void main(String[] args) throws Exception {
    
    String userProfile = System.getProperty("user.home");
    System.out.println( "接続情報は下記ファイルから取得します。" );
    String resourcepath = String.format("%s\\_comture\\jdbc.properties", userProfile);
    System.out.println( resourcepath );

    Properties props = new Properties();        
    InputStream inputStream = new FileInputStream(new File(resourcepath));
    props.load(inputStream);
    
    // ★ここからMyBatis利用コード
    // 設定ファイルを読み込む ※MyBatisを利用する側のプロジェクトで用意する。
    try (InputStream in = TestMain.class.getResourceAsStream("/mybatis-config.xml")) {
      try (InputStream in = new FileInputStream(resourcepath)) {
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in, props);
        try (SqlSession session = factory.openSession()) {
           CustomerMapper mapper = session.getMapper(CustomerMapper.class);
           CustomerExample params = new CustomerExample();
           params.createCriteria().andCustomerIdEqualTo("001");
           List<Customer> result = mapper.selectByExample(params);
           // ラムダ式はJava8以降の記述なので注意。
           result.forEach(row -> {
              System.out.println("CustomerId: " + row.getCustomerId());
           });
        }
     }
   }
}

自動生成の際には以下の点に注意してください。
MyBatis generatorは自動生成後、各テーブルごとにSQLMap, マッパークラス、モデルクラスを作成します。そして、各メソッド単位に@mbggeneratedというアノテーションを付加します。

これは次回実行時に自動生成対象として再作成する対象のヒントとなるものです。なのでこれを消せば、次回はメソッドが上書きされません。

しかし、それらのファイルにカスタマイズとしてメソッドを追加し、自動生成を再度行う(出力先をカスタマイズ後のソースを格納しているフォルダに指定)と、SQLMapはカスタマイズ部分が残りますが、マッパークラスはクリアされてしまいました。

なぜなのか。仕様なのかバグなのか。なんじゃこりゃ。

この問題に対し、マッパークラスを利用しない手があります。
マッパーを利用しないアクセス方法は以下のような感じです。

List<Customer> result = session.selectList("com.common.dao.CustomerMapper.selectByExample", params);

これならマッパークラスが書き変わろうが影響ありません。
まあ、個人的にはマッパークラス経由でのアクセスの方が好ましいと思いますが。

また、出力されるファイルの名前などを変えたい時には自分でプラグインをgeneratorに差し込むことで対応できます。ただ、ちょっとハードル上がりますw

ちなみにMyBatisになって、昔のiBatisとは記述が少し変わった部分があったりするのでバージョンアップなどの移行の際には気をつけてください。

それではよいDAOライフを。

コメントを残す

メールアドレスが公開されることはありません。