こんにちは。
キャスレーコンサルティングSI(システム・インテグレーション)部の西川です。

すでに沢山の解説サイトがあるなかでいまさら・・・とか言われそうですが、
今回はJavaのO/RMapperフレームワークであるMyBatisを使ってDBにアクセスしてみたいと思います。

DB関連の学習ってテーブル用意して・・・データ用意して・・・といった若干面倒な準備が必要なので
既存のDBなどを使って簡単に動かす方法ないかなと思ったことはありませんか?

既にMyBatisの使い方を解説されているサイトはたくさんありますが、
今回は以下のテーマでMyBatisを動かしてみたいと思います。

  • MyBatisが動くEclipseプロジェクトを時間をかけずに作る
  • サンプルや既存のDB環境をそのまま流用する

MyBatisの真価はマッピング機能や動的SQL、SpringFrameworkなどといったDIコンテナとの連携で発揮される
とは思いますが、まずは入門編ということでご了承ください。

1.はじめに

MyBatisとは

MyBatisとはJavaのフレームワークのひとつで、O/RMapperという分類になります。
O/RMapperとは、Object/Relational Mapperの略で、ざっくり言うとJava(Object)と
関係データベース(Relational)を紐付ける機能(Mapper)です。

表形式であるSQLの実行結果をどのようにJavaのクラスに当てはめるか、
またJavaから受け渡されたパラメータをどのようにSQLに反映するかといった設定をしておくことが出来ます。

これによりJavaのプログラムからSQLを発行して、実施結果を取得する際、
Java側はSQL部分を意識せずに処理を記述することができ、
逆にSQL側はJavaを意識せずにSQL文を記述できるのが利点です。

MyBatisはそのほかにSpringFrameworkなどアノテーションを利用したDI(依存性の注入)がサポートされていたり、
XMLに記載したSQLにパラメータや分岐、繰り返しなどの制御を加えることが可能になっています。

詳しくはこちら(公式:http://www.mybatis.org/mybatis-3/ja/

今回の目的

  • MyBatisというフレームワークを使ってDB操作をしてみる
  • MySQLなどに同梱されているサンプルのDBを使用し、DB構築、データ準備の手間を省く

事前準備

  • Java SE 8 (JDK1.8)
  • Eclipse4.4 (Maven※1 を使うのでご使用のEclipseにMavenプラグインが無ければ別途インストールしてください。面倒なかたはAllInOneEclipseでも大丈夫です。 Pleiades公式:https://mergedoc.osdn.jp/)
  • MySQL Community Server5.7 (公式:https://www-jp.mysql.com/)
  • Sakilaデータベース※2(MySQLがインストールされているのにいない場合はこちらからダウンロードできます:http://dev.mysql.com/doc/index-other.html

なお、それぞれのインストール手順は割愛いたします。

※1 Mavenはプロジェクト管理ツールです。ここではMyBatisなどのライブラリを取得してビルド環境の構築に使います。
※2 sakilaは映画のレンタルショップを想定したテーブルが配置されています。

それでは早速始めていきましょう。

2.プロジェクトの作成

  • プロジェクトの新規作成を選択し,「Mavenプロジェクト」を選択します。
    000017
  • そのまま「次へ」を選択します。
    000018
  • 「maven-archetype-quickstart」を選択します。
    000019
  • アーティファクトID(プロジェクト名)を入力します。ここでは「MyBatisSample」としました。
    000020
  • 「完了」ボタン押下後、しばらくするとプロジェクトが展開されます。
    000021_2
  • src/main/resourcesフォルダを作成し,ソースフォルダに設定します。
    000025_2

これでプロジェクトのセットアップは完了です。

3.必要なAPIのインストール

Mavenを使っているので、MyBatisやMySQLのJDBCライブラリはMavenに準備してもらいます。
プロジェクトセットアップ時に自動で作成されたpom.xmlに必要なライブラリの情報を記載することで、
ライブラリのダウンロードとビルドパスへの反映を行うことが出来ます。

  • pom.xmlを修正します
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.example</groupId>
	<artifactId>MyBatisSample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>MyBatisSample</name>
	<url>http://maven.apache.org</url>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

		<!-- http://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.33</version>
		</dependency>

		<!-- http://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.0</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.mybatis.generator</groupId>
				<artifactId>mybatis-generator-maven-plugin</artifactId>
				<version>1.3.1</version>
			</plugin>
		</plugins>
	</build>
</project>

ハイライトした行が追加した部分です。

dependencyタグ内にプロジェクトで使用するライブラリを記載し、
pluginタグにEclipseで開発する際に使用するプラグインを記載します。
なお、これらの追加作業はpom.xmlを右クリックし、maven>依存情報の追加(プラグインの追加)を選択することで
ダイアログから追加することもできます。

  • pom.xmlを右クリックし、実行>「3 maven Install(3)」を選択します。
    000057
    この操作でMavenのリポジトリに登録されているライブラリをダウンロードし、ビルドパスに追加してくれます。

4.エンティティクラスの自動生成

MyBatisを使用してDBアクセスするために必要なものは以下の3つです。

  • テーブル、またはビューのエンティティクラス
  • 使用するエンティティとSQLを記載したMapper.xml
  • Mapper.xmlと同名のJavaインターフェース

しかし、これらを手動で作成するとなかなか面倒です。
特にエンティティクラスは構造としては単純ですが、
既存のDBのテーブル数やカラム数が多い場合は用意するのも嫌になってくると思います。

JPAなどDBからエンティティクラスを作成するプログラムは様々ありますが、
今回はエンティティと一緒にマッピング部分まで作成してくれるMyBatisの機能MyBatis Generatorを使用しましょう。

MyBatis Generatorを実行すると既存のDBから以下を自動生成することができます。

  1. テーブル、またはビューのエンティティクラス
  2. MapperのJavaインターフェース
  3. 使用するエンティティとSQLを記載したMapper.xml
  4. JavaからWhere句を指定してSQLを発行するための実装例

それでは実際に作成してみましょう。

generatorConfig.xmlの作成

MyBatis Generatorの設定ファイルを作成しましょう。
「resources」フォルダ直下に「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="C:\pleiades\workspace2\webtest\target\webtest\WEB-INF\lib\mysql-connector-java-5.1.33.jar" />
	<context id="context1">

<!-- 	JDBCの設定です (1) -->
		<jdbcConnection
			driverClass="com.mysql.jdbc.Driver"
			connectionURL="jdbc:mysql://localhost:3306/sakila"
			userId="root"
			password="root"
		/>

<!-- 	自動生成するエンティティの設定です (2) -->
		<javaModelGenerator
			targetPackage="com.example.entity"
			targetProject="src/main/java/"
		/>
		<sqlMapGenerator
			targetPackage="com.example.entity"
			targetProject="src/main/java/"
		/>
		<javaClientGenerator
			targetPackage="com.example.entity"
			targetProject="src/main/java/"
			type="XMLMAPPER"
		/>

<!-- 	生成対象のテーブルです(3) -->
		<table schema="sakila" tableName="%" />
	</context>
</generatorConfiguration>

8-14行目はJavaでよく見るJDBCの接続情報です。
環境に合わせて修正してください。

16-29行目は自動作成するファイルの情報です。
ここで出来上がるJavaクラスのパッケージ宣言などが決定します。

31-32行目はどのテーブルを対象にするかを指定します。
ビューを指定することも出来ます。
今回はワイルドカードの%を指定しているので全テーブル、ビューが対象になります。

tableタグは複数書くことが出来るので、以下のように記載すれば、

<table schema="sakila" tableName="Actor" />
<table schema="sakila" tableName="Film%" />

「Actorテーブル」と「Filmで始まるテーブル(とビュー)すべて」のみを対象にすることができます。

Mavenビルドで自動生成

作成したgeneratorConfig.xmlを使って自動生成を行うにはMavenビルドを行います。
メニューの実行>実行構成を選択し、「実行構成ダイアログ」を表示し、Mavenビルドを新規作成します。

「基底ディレクトリー」欄には「ワークスペースの参照」ボタンを選択して
今回作成したプロジェクト「MyBatisSample」を選択してください。
「ゴール」欄には「mybatis-generator:generate」と入力し、実行ボタンを選択します。
000030

実行後、プロジェクトをリフレッシュすると、先ほど「generatorConfig.xml」で指定した
「com.example.entity」パッケージに自動生成されたファイルが出来上がったことを確認できます。
000033

先ほど、自動生成されるファイルは以下と説明しました。

  1. テーブル、またはビューのエンティティクラス
  2. MapperのJavaインターフェース
  3. 使用するエンティティとSQLを記載したMapper.xml
  4. JavaからWhere句を指定してSQLを発行するための実装例

これらは1テーブル(またはビュー)毎にそれぞれ作成されます。

sakilaの「actor」テーブルから生成されたファイルについて確認しましょう。
参考までに「actor」テーブルのデータ構造は以下のようになっています。

CREATE TABLE `actor` (
`actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`first_name` varchar(45) NOT NULL,
`last_name` varchar(45) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`actor_id`),
KEY `idx_actor_last_name` (`last_name`)
) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8

テーブル、またはビューのエンティティクラス

public class Actor {

    private Short actorId;

    private String firstName;

    private String lastName;

    private Date lastUpdate;
}

これは「actor」テーブルを検索した場合に返却されるエンティティクラスです。
ここでは省略していますが、JavaBeansの形ですので各privateメンバに対してgetter/setterメソッドが実装されています。
「actor」テーブルのカラムをメンバとして持ち、1インスタンスで1レコードを表すことが出来ます。

また、Insert/Update/Deleteを行う場合もこちらのクラスを使う場合があります。

MapperのJavaインターフェース

public interface ActorMapper {

    List<Actor> selectByExample(ActorExample example);

    Actor selectByPrimaryKey(Short actorId);

}

これはMapperのJavaインターフェースです。
Java側はこのインターフェースのメソッドを呼び出すことでDBへの操作を行うことが出来ます。
このインターフェースに宣言したメソッドに対応するMapper.xml上のSQL定義が必須となります。
なお、使用時はこのインターフェースを実装したクラスをMyBatisが自動的にインスタンス化してくれるため
準備は不要です。

使用するエンティティとSQLを記載したMapper.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="com.example.entity.ActorMapper" >

  <resultMap id="BaseResultMap" type="com.example.entity.Actor" >
    <id column="actor_id" property="actorId" jdbcType="SMALLINT" />
    <result column="first_name" property="firstName" jdbcType="VARCHAR" />
    <result column="last_name" property="lastName" jdbcType="VARCHAR" />
    <result column="last_update" property="lastUpdate" jdbcType="TIMESTAMP" />
  </resultMap>

  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.example.entity.ActorExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from actor
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Short" >
    select
    <include refid="Base_Column_List" />
    from actor
    where actor_id = #{actorId,jdbcType=SMALLINT}
  </select>
</mapper>

Mapperインターフェースと対になるXMLファイルです。
先ほどからMapper.xmlと表現しているものはこれを指しています。
このファイルには、以下の3つの設定について記載することが出来ます。

  • DBから取得した値をどのエンティティに格納するかのマッピング定義
  • Javaから渡された値をどのように解釈するかのマッピング定義
  • Mapperインターフェースに宣言されたメソッドに対応するSQL

JavaからWhere句を指定してSQLを発行するための実装例

public class ActorExample {

    public ActorExample() {
        oredCriteria = new ArrayList<Criteria>();
    }

    public Criteria or() {
        Criteria criteria = createCriteriaInternal();
        oredCriteria.add(criteria);
        return criteria;
    }

    public Criteria createCriteria() {
        Criteria criteria = createCriteriaInternal();
        if (oredCriteria.size() == 0) {
            oredCriteria.add(criteria);
        }
        return criteria;
    }

    protected abstract static class GeneratedCriteria {
        protected List<Criterion> criteria;

        public Criteria andActorIdGreaterThan(Short value) {
            addCriterion("actor_id >", value, "actorId");
            return (Criteria) this;
        }

        public Criteria andActorIdLessThan(Short value) {
            addCriterion("actor_id <", value, "actorId");
            return (Criteria) this;
        }

        public Criteria andFirstNameLike(String value) {
            addCriterion("first_name like", value, "firstName");
            return (Criteria) this;
        }

        public Criteria andLastNameLike(String value) {
            addCriterion("last_name like", value, "lastName");
            return (Criteria) this;
        }
    }

    public static class Criteria extends GeneratedCriteria {

        protected Criteria() {
            super();
        }
    }

    public static class Criterion {

    }
}

(クリックで表示します)
このクラスはJavaからWhere句を指定してSQLを発行するためのクラスです。
「MyBatisを使うとこんなことも出来ますよ」といった実装例のクラスですので詳細は割愛いたします。
簡単な使用方法は5章にて解説いたします。

5.MyBatisを動かすための設定ファイルを作成する

「resources」フォルダ直下に以下の「mybatis-config.xml」を作成してください。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

<!-- 	JDBCの設定です(1) -->
	<environments default="sakila">
		<environment id="sakila">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost/sakila" />
				<property name="username" value="root" />
				<property name="password" value="root" />
			</dataSource>
		</environment>
	</environments>

<!-- 	エンティティのパッケージを指定します(2) -->
	<mappers>
		<package name="com.example.entity" />
	</mappers>

</configuration>

7-18行目にDBへの接続情報を記載します。
「generatorConfig.xml」で記述したタグ構造とは異なりますが、記載する内容は同様です。

20-23行目にMapperクラスが格納されているパッケージを記載します。

また、packageタグではなく、mapperタグを使うことで使用するMapperインターフェースを
単独で指定することも出来ます。
もちろんpackageタグとmapperタグはそれぞれ複数回定義可能です。

6.MyBatisを動かしてみる

これでMyBatisを実際に動かすための準備が出来ました。
実際に動かしてみましょう。

MyBatisの大まかな使い方は以下となります。

  1. 「mybatis-config.xml」をInputStreamやReaderに読み込みます。
  2. SqlSessionFactoryBuilderに 1 を渡してSqlSessionFactoryを作成します。
  3. SqlSessionFactoryからSessionを開きます。
  4. Sessionに使いたいMapperインターフェースを指定し、Mapperインターフェースのインスタンスを取得します。
  5. Mapperのメソッドを呼ぶことでMyBatisがSQLを発行し、結果を取得します。

2 まではアプリケーションの起動時に1回行ってあとは使いまわすことが出来ます。
3 以降は使用時に適宜取得し、使用後は破棄するようにしましょう。

主キーを使った検索

以下はMyBatisを動作させるためのプログラムです。

package com.example.MyBatisSample;

import java.io.IOException;
import java.io.Reader;

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

import com.example.entity.Actor;
import com.example.entity.ActorMapper;

/**
 * MyBatisを使ってDBにアクセスするサンプルプログラムです.
 */
public class App {
    public static void main(String[] args) {

        // resources直下のmybatis-config.xmlを読み込みます(1)
        try (Reader r = Resources.getResourceAsReader("mybatis-config.xml");) {

            // 読み込んだ設定ファイルからSqlSessionFactoryを生成します(2)
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(r);

            // SQLセッションを取得します(3)
            try (SqlSession session = factory.openSession()) {

                // ActorテーブルのMapperを取得します(4)
                ActorMapper map = session.getMapper(ActorMapper.class);
                // Actorテーブルの主キー(actor_id)が1であるレコードを検索します(5)
                Actor actor = map.selectByPrimaryKey((short) 1);

	      // 取得した内容を確認します
                System.out.println("actor.getActorId    = " + actor.getActorId());
                System.out.println("actor.getFirstName  = " + actor.getFirstName());
                System.out.println("actor.getLastName   = " + actor.getLastName());
                System.out.println("actor.getLastUpdate = " + actor.getLastUpdate());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

21行目で使用しているResourcesクラスはMyBatisのクラスになります。
「resources」フォルダ配下のファイルを読み込むことが出来ます。

23行目で読み込んだ設定ファイルをSqlSessionFactoryBuilderに渡してSqlSessionFactoryを生成しています。

27行目でSqlSessionFactoryからSQLセッションを開きます。

30行目では使用するMapperインターフェース(ここではActorMapper)の型を渡して
ActorMapperを実装したインスタンスを受け取ります。
ActorMapperを実装した具象クラスとインスタンスはMyBatisが用意するので、
利用者はインスタンスを受け取るだけでよいのです。

32行目のように、ActorMapperインターフェースに記述されているメソッドを呼ぶことでDBアクセスが行われます。「MyBatis Generator」で自動生成した場合、Select系のメソッドには以下の2種類が用意されていると思います。

  • selectByPrimaryKey(short):主キーから一意のレコードを取得します。
  • selectByExample(ActorExample):全体から条件に沿うレコードを取得します。

ここではselectByPrimaryKeyを使い、主キー(actor_id)が1であるレコードを取得しています。
早速実行して結果を確認してみましょう。
000037
上の部分は実際のサンプルテーブルで、下が取得した内容を標準出力に表示した文字列です。

actor_idが’1’のデータを取得することが出来ました。

Exampleクラスを使った検索

続いてselectByExampleを使ってみましょう。
selectByExampleメソッドに渡すExampleクラスはソースだけ見ると何をやっているのかほとんどわからないと思います。
selectByExampleメソッドは、Mapper.xmlのSQLにはWhere句を書かず、Java側でWhere句にあたる部分を記述して
検索条件とすることができます。

package com.example.MyBatisSample;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

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

import com.example.entity.Actor;
import com.example.entity.ActorExample;
import com.example.entity.ActorMapper;

/**
 * MyBatisを使ってDBにアクセスするサンプルプログラムです.
 *
 */
public class App2 {
    public static void main(String[] args) {

        // resources直下のmybatis-config.xmlを読み込みます
        try (Reader r = Resources.getResourceAsReader("mybatis-config.xml");) {

            // 読み込んだ設定ファイルからSqlSessionFactoryを生成します
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(r);

            // SQLセッションを取得します
            try (SqlSession session = factory.openSession()) {

                // ActorテーブルのMapperを取得します
                ActorMapper map = session.getMapper(ActorMapper.class);

                // Actorテーブルの条件検索用クラスを生成します
                ActorExample ex = new ActorExample();

                // WHERE
                //    (first_name LIKE 'T%' AND actor_id < 100)
                //    OR (last_name LIKE 'S%' AND actor_id > 100)
                // 検索条件に↑と同等の条件を設定しています
                //    Criteriaを作成し、AND条件を追加する (1)
                ex.createCriteria().andFirstNameLike("T%").andActorIdLessThan((short)100);
                // 2 OR条件がある場合はExampleのor() メソッドで区切り、OR条件内の条件を追加する
                ex.or().andLastNameLike("S%").andActorIdGreaterThan((short)100);

                // 上記の条件でテーブルを検索します
                List<Actor> actorList = map.selectByExample(ex);

                // 取得結果を表示します
                System.out.println("actor_id, first_name, last_name, last_update");
                for (Actor actor : actorList) {

                    System.out.printf("%s, %s, %s, %s \n", actor.getActorId(), actor.getFirstName(), actor.getLastName(),
                                    actor.getLastUpdate());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

取得結果を見てみましょう。
上部はActorテーブルに以下の条件のフィルターをかけた状態の表示内容です。
(first_name LIKE ‘T%’ AND actor_id < 100) OR (last_name LIKE ‘S%’ AND actor_id > 100)
000046

同様の値が取れていることが確認できました。

1テーブルに対するWHERE句の指定のみであればそのまま使うことも十分可能でしょう。
結合や副問い合わせ、集計を行いたい場合もあると思いますが、その場合は自分でSQLを実装する必要があります。

6.最後に

いかがでしたでしょうか?
DB関連のプログラムを勉強しようとしても、DBの構築は1からやると結構面倒なのでそういった部分を
極力軽減できればと思い記事にしてみました。
今回ご紹介した機能はMyBatisのほんのさわりの部分です。

次回の記事では、MyBatisの肝と言っても過言ではない以下の2機能についてご紹介したいと思います。

  • エンティティクラスとMapper.xmlを自作してSQLの結果を実際にマッピングしてみる
  • 動的SQLを使ってみる

どうぞお楽しみに!