MyBatis とは
SQL と Java オブジェクトを紐付ける永続化フレームワーク。
以前は iBATIS という名前で Apache プロジェクトの1つとして開発されていた。
しかし、 2010年6月に Apache ソフトウェア財団での開発が中止され、現在は MyBatis という名前で開発されている。
SQL 文を完全にコントロールしたい場合に使いやすいらしい。
環境
OS
Windows 7 64bit
Java
1.8.0_65
データベース
MySQL 5.5.28
Hello World
インストール
build.gradle
compile 'org.mybatis:mybatis:3.3.0'
compile 'mysql:mysql-connector-java:5.1.38'
データベースの準備
フォルダ構成
|-src/main/ | |-java/ | | `-sample/mybatis/ | | `-Main.java | `-resources/ | |-mybatis-config.xml | `-sample_mapper.xml `-build.gradle 設定ファイル
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>
<environments default="sample_id">
<environment id="sample_id">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/test"/>
<property name="username" value="test"/>
<property name="password" value="test"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sample_mapper.xml"/>
</mappers>
</configuration>
sample_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="sample.mybatis">
<select id="selectTest" resultType="map">
select * from test_table </select>
</mapper>
実装
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
// ★ルートとなる設定ファイルを読み込む
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
// ★設定ファイルを元に、 SqlSessionFactory を作成する
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// ★SqlSessionFactory から SqlSession を生成する
try (SqlSession session = factory.openSession()) {
// ★SqlSession を使って SQL を実行する
List<Map<String, Object>> result = session.selectList("sample.mybatis.selectTest");
result.forEach(row -> {
System.out.println("---------------");
row.forEach((columnName, value) -> {
System.out.printf("columnName=%s, value=%s%n", columnName, value);
}); }); } } } }
実行結果
--------------- columnName=id, value=1 columnName=value, value=hoge --------------- columnName=id, value=2 columnName=value, value=fuga --------------- columnName=id, value=3 columnName=value, value=piyo 説明
· 設定ファイル
· まず、データベースとの接続情報などを定義したメインとなる設定ファイルを用意する(mybatis-config.xml)。
· 次に、 SQL 文や検索結果のマッピング方法などを定義したファイルを用意する(sample_mapper.xml)。
· 実装
· まず、 SqlSessionFactoryBuilder を使って、設定ファイル(mybatis-config.xml)を読み込む。
· SqlSessionFactory を使って、 SqlSession を生成する。
· SqlSession に用意されている SQL 実行用のメソッドを使って SQL を実行する(selectList())。
· 第一引数には、実行する SQL を識別するための ID (ステートメントID)を指定する。
· ステートメント ID や戻り値の型は、 sample_mapper.xml で定義された情報が元になっている。
各インスタンスの推奨スコープ
| インスタンス | 説明 | 推奨スコープ |
| | | メソッドスコープ |
| | 一度作ったら、それを使い回すべき。 | アプリケーションスコープ |
| | 非スレッドセーフ。一連の SQL 実行が終わったらクローズする。 | リクエストスコープ |
| | | メソッドスコープ |
※Mapper については こちら を参照。
· メソッドスコープ
· メソッド内だけで使い捨てる。
· リクエストスコープ
· Web アプリで、1つのリクエストの間だけ同じインスタンスを使い回す。
· 他のリクエストからは参照させないようにする。
· アプリケーションスコープ
· アプリケーションが動いている間ずっと同じインスタンスを使い回す。
設定ファイル
外部のプロパティファイルを読み込む
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>
<properties resource="hoge.properties">
<property name="hoge" value="property tag"/>
<property name="fuga" value="property tag"/>
<property name="piyo" value="property tag"/>
</properties>
</configuration>
hoge.properties
fuga=property file
piyo=property file
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.Properties;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
Properties prop = new Properties(); prop.put("piyo", "Properties Class");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in, prop);
Properties properties = factory.getConfiguration().getVariables();
properties.forEach((key, value) -> {
System.out.printf("%s=%s%n", key, value);
}); } } }
実行結果
hoge=property tag fuga=property file piyo=Properties Class · 以下の場所でプロパティを定義できる。
1. <properties> タグ内の <property> タグ。
2. <properties> タグで読み込んだプロパティファイル。
3. SqlSessionFactory を生成するときに Properties インスタンスを渡す。
· 各定義は上述の順序で読み込まれ、後で読み込まれたもので上書きされる。
· <properties> タグで別ファイルを読み込む場合は、 resource 属性でリソースファイルを、 url 属性で URL 指定でファイルを読み込める。
· url 属性で指定する場合は file:///C:/foo/bar/hoge.properties みたいな感じで指定する。
· 読み込んだプロパティは、 ${...} という形で参照できる。
複数のデータベース接続を定義する
データベースの接続先情報は、 environment という単位で定義する。
SqlSessionFactory は、この environment 単位でインスタンスを生成することになる。
デフォルトでは <environments> タグの default 属性で指定した environment が使用される。
しかし、 SqlSessionFactory を生成するときに任意の environment を指定することもできる。
test_table @ hoge スキーマ
test_table @ fuga スキーマ
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>
<environments default="hoge"> <!-- ★hoge をデフォルト指定 -->
<environment id="hoge">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/hoge"/>
<property name="username" value="test"/>
<property name="password" value="test"/>
</dataSource>
</environment>
<environment id="fuga">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/fuga"/>
<property name="username" value="test"/>
<property name="password" value="test"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sample_mapper.xml"/>
</mappers>
</configuration>
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.function.Function;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
execute(in -> new SqlSessionFactoryBuilder().build(in)); // ★environment を指定しない
execute(in -> new SqlSessionFactoryBuilder().build(in, "fuga")); // ★environment を指定
} private static void execute(Function<InputStream, SqlSessionFactory> sqlSessionFactorySupplier) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = sqlSessionFactorySupplier.apply(in);
try (SqlSession session = factory.openSession()) {
System.out.println(session.selectList("sample.mybatis.selectTest"));
} } System.out.println();
} }
実行結果
[{id=1, value=fizz}, {id=2, value=buzz}] [{id=1, value=hoge}, {id=2, value=fuga}, {id=3, value=piyo}] · SqlSessionFactoryBuilder#build() の第二引数で、 environment を指定することができる。
Java EE サーバーに管理されたデータソースを使用する
Java EE サーバー上で動かす場合、データソースはサーバーに登録しておいて、トランザクション制御は Java EE コンテナに任せることができる。
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>
<environments default="test">
<environment id="test">
<transactionManager type="MANAGED"/> <!-- ★type を MANAGED にする -->
<dataSource type="JNDI"> <!-- ★JNDI にする -->
<!-- ★data_source で JNDI からルックアップするための名を指定する -->
<property name="data_source" value="java:app/sampleDS"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="sample_mapper.xml"/>
</mappers>
</configuration>
· <transactionManager> タグの type を MANAGED にする。
· <datasource> タグの type を JNDI にする。
· <property> タグで、 data_source を設定する。
· 値は JNDI ルックアップするための名前にする。
実行される SQL をログに出力する
logback の依存関係を追加すれば、 DEBUG レベルで出力される。
build.gradle
compile 'ch.qos.logback:logback-classic:1.1.3'
ログ出力
18:10:01.123 [main] DEBUG sample.mybatis.selectTest - ==> Preparing: select * from test_table where id=? and id=? 18:10:01.151 [main] DEBUG sample.mybatis.selectTest - ==> Parameters: 1(Integer), 1(Integer) 18:10:01.165 [main] DEBUG sample.mybatis.selectTest - <== Total: 1 実行された SQL と、バインドされたパラメータが出力されている。
実行した SQL のステートメント ID (sample.mybatis.selectTest)がロガー名になっているので、ステートメント ID を利用したログ出力の制御ができるようになっている。
検索
複数件の結果を取得する
sample_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="sample.mybatis">
<select id="selectTest" resultType="map">
select * from test_table </select>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
List<Map<String, Object>> result = session.selectList("sample.mybatis.selectTest");
System.out.println(result);
} } } }
実行結果
[{id=1, value=fizz}, {id=2, value=buzz}] · 検索用の SQL は、 <select> タグで宣言する。
· id 属性で、その SQL を参照するための識別名を設定する。
· resultType 属性で、検索結果のレコードをどの型にマッピングするかを定義する。
· ここでは map と指定しているので、 Map 型に変換される。
· 検索結果が複数のレコードを返す場合、 SqlSession の selectList() メソッドを使用する。
· 戻り値は List 型になる。
単一の結果を取得する
sample_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="sample.mybatis">
<select id="selectTest" resultType="map">
select * from test_table limit 1 </select>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
Map<String, Object> result = session.selectOne("sample.mybatis.selectTest");
System.out.println(result);
} } } }
実行結果
{id=1, value=fizz} · 検索結果が1件だけで単一のオブジェクトで取得したい場合は、 selectOne() メソッドを使用する。
· もし検索結果が複数のレコードを返した場合、 TooManyResultsException がスローされる。
· 検索結果が 0 件の場合は null を返す。
resultType で指定できる値
resultType には、検索結果を Java のどの型に変換するかを設定する。
通常は java.util.Map のように変換先クラスの FQCN を指定する。
しかし、一部のよく利用する型については予めエイリアスが定義されており、短い名前で指定できる。
以下、 org.apache.ibatis.type.TypeAliasRegistry より抽出。
| エイリアス | 型 |
| string | String |
| date | Date |
| map | Map |
| hashmap | HashMap |
| list | List |
| arraylist | ArrayList |
| decimal | BigDecimal |
| bigdecimal | BigDecimal |
| biginteger | BigInteger |
| _byte | byte |
| _long | long |
| _short | short |
| _int | int |
| _integer | int |
| _double | double |
| _float | float |
| _boolean | boolean |
| _byte[] | byte[] |
| _long[] | long[] |
| _short[] | short[] |
| _int[] | int[] |
| _integer[] | int[] |
| _double[] | double[] |
| _float[] | float[] |
| _boolean[] | boolean[] |
| byte | Byte |
| long | Long |
| short | Short |
| int | Integer |
| integer | Integer |
| double | Double |
| float | Float |
| boolean | Boolean |
| byte[] | Byte[] |
| long[] | Long[] |
| short[] | Short[] |
| int[] | Integer[] |
| integer[] | Integer[] |
| double[] | Double[] |
| float[] | Float[] |
| boolean[] | Boolean[] |
| object | Object |
| date[] | Date[] |
| decimal[] | BigDecimal[] |
| bigdecimal[] | BigDecimal[] |
| biginteger[] | BigInteger[] |
| object[] | Object[] |
| collection | Collection |
| iterator | Iterator |
| ResultSet | ResultSet |
プリミティブ型の場合は _ を前に付ける。付けない場合はラッパークラスになるので注意。
エイリアスを定義する
エイリアスは任意に定義できる。
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>
<typeAliases>
<typeAlias type="foo.bar.Hoge" alias="Hoge" />
</typeAliases>
... </configuration>
· メインの方の設定ファイル(mybatis-config.xml)に <typeAlias> タグで宣言できる。
パッケージを指定してエイリアスを定義する
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>
<typeAliases>
<package name="foo.bar"/>
</typeAliases>
... </configuration>
· <package> タグを使うことで、パッケージ単位でエイリアスを定義できる。
· この場合、クラス名がそのままエイリアスになる。
パラメータ
単一の場合
sample_mapping.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="sample.mybatis">
<select id="selectTest" resultType="map">
select * from test_table where id=#{id} </select>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
Map<String, Object> result = session.selectOne("sample.mybatis.selectTest", 1);
System.out.println(result);
} } } }
実行結果
[DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where id=? [DEBUG] s.m.selectTest - ==> Parameters: 1(Integer) [DEBUG] s.m.selectTest - <== Total: 1 {id=1, value=fizz} · #{...} という形式でパラメータを宣言できる。
· パラメータは、 SqlSession で SQL を実行するときに、ステートメント ID (sample.mybatis.selectTest)の次に引数で渡す。
複数の場合
sample_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="sample.mybatis">
<!-- ★パラメータの型を指定する -->
<select id="selectTest" resultType="map" parameterType="sample.mybatis.MyParameter">
select * from test_table where id=#{id} and value=#{value} </select>
</mapper>
MyParameter.java
package sample.mybatis;
public class MyParameter {
private int id;
private String value; public MyParameter(int id, String value) {
this.id = id;
this.value = value;
} public int getId() {
System.out.println("get id");
return id; } }
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
MyParameter param = new MyParameter(2, "aaa");
Map<String, Object> result = session.selectOne("sample.mybatis.selectTest", param);
System.out.println(result);
} } } }
実行結果
get id [DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where id=? and value=? get id [DEBUG] s.m.selectTest - ==> Parameters: 2(Integer), aaa(String) · 複数のパラメータを埋め込む場合、何かしらのクラスのインスタンスに値を入れて、それを渡すようにする。
· 入れ物となるクラスは、 <select> タグの parameterType 属性で型を指定しておく。
· 宣言したパラメータの名前と一致するフィールド(プロパティ)の値がインスタンスから取得される。
· 値は、 Getter メソッドがあればそれ経由で、なければフィールドから直接取得される(private でも取得される模様)。
Map でパラメータを渡す
複数のパラメータを渡すには、 Map を使う方法も用意されている。
sample_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="sample.mybatis">
<select id="selectTest" resultType="map">
select * from test_table where id=#{id} and value=#{value} </select>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
Map<String, Object> param = new HashMap<>(); param.put("id", 10);
param.put("value", "hogeeee");
Map<String, Object> result = session.selectOne("sample.mybatis.selectTest", param);
System.out.println(result);
} } } }
実行結果
[DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where id=? and value=? [DEBUG] s.m.selectTest - ==> Parameters: 10(Integer), hogeeee(String) · パラメータは Map でも渡すことができる。
· その場合は、 <select> タグの parameterType の設定は無くても動く。
特殊なパラメータ名
以下のパラメータ名は、 MyBatis によって自動で登録される特殊なパラメータ名で、暗黙的に利用できる。
| パラメータ名 | 条件 |
| | (たぶん)常に参照可能 |
| | パラメータが |
| | パラメータが |
| | パラメータが配列の場合 |
· _parameter を設定している場所。
· org.apache.ibatis.scripting.xmltags.DynamicContext
· collection, list, arrary を設定している場所。
· org.apache.ibatis.session.defaults.DefaultSqlSession#wrapCollection(Object)
検索結果を任意の Java オブジェクトにマッピングする
基本
sample_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="sample.mybatis">
<select id="selectTest" resultType="sample.mybatis.TestTable">
select * from test_table </select>
</mapper>
TestTable.java
package sample.mybatis;
public class TestTable {
private int id;
private String value; public void setValue(String value) {
System.out.println("setValue(" + value + ")");
this.value = value;
} @Override
public String toString() {
return "TestTable [id=" + id + ", value=" + value + "]";
} }
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
List<TestTable> result = session.selectList("sample.mybatis.selectTest");
System.out.println(result);
} } } }
実行結果
[DEBUG] s.m.selectTest - ==> Preparing: select * from test_table [DEBUG] s.m.selectTest - ==> Parameters: setValue(fizz) setValue(buzz) [DEBUG] s.m.selectTest - <== Total: 2 [TestTable [id=1, value=fizz], TestTable [id=2, value=buzz]] · resultType に検索結果をマッピングしたい Java クラスを指定する。
· カラム名と一致するフィールドに値がセットされる。
· 値のセットは、 Setter メソッドがあればそれ経由で、なければフィールドに直接行われる。
スネークケースとキャメルケースのマッピングを自動化させる
test_table
TestTable.java
package sample.mybatis;
public class TestTable {
private int id;
private String testValue; @Override
public String toString() {
return "TestTable [id=" + id + ", testValue=" + testValue + "]";
} }
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>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
... </configuration>
sample_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="sample.mybatis">
<select id="selectTest" resultType="sample.mybatis.TestTable">
select * from test_table </select>
</mapper>
selectTestの実行結果
TestTable [id=1, testValue=hoge] TestTable [id=2, testValue=fuga] TestTable [id=3, testValue=piyo] · mapUnderscoreToCamelCase に true を設定することで、スネークケースとキャメルケースの自動変換をしてくれるようになる。
· デフォルトは false になっている。
クラスごとにマッピングを定義する
sample_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="sample.mybatis">
<resultMap id="testTableResultMap" type="sample.mybatis.TestTable">
<id property="id" column="id" />
<result property="value" column="value" />
</resultMap>
<select id="selectTest" resultMap="testTableResultMap">
select * from test_table </select>
</mapper>
· <resultMap> タグを使って、検索結果と Java クラスとのマッピングを定義できる。
· <id> タグで、識別子プロパティを定義する。
· 識別子プロパティは、インスタンスを識別するときに使用するプロパティを指す。
· 識別子プロパティを指定しておくことで、キャッシュや JOIN マッピングのときにパフォーマンスが向上するらしい。
· property 属性で、 Java 側のプロパティ(フィールド)名を指定する。
· column 属性で、データベース側の列名を指定する。
· <result> タグで、各列のマッピングを定義する。
· こちらも property と column で名前をマッピングする。
· <resultMap> の id 属性でマッピングを一意に特定するための名前を定義する(testTableResultMap)。
· この名前を、 <select> タグの resultMap 属性に指定する。
単一のオブジェクトをマッピングする
基本
test_table
TestTable.java
package sample.mybatis;
public class TestTable {
private int id;
private String string; private Embedded embedded; @Override
public String toString() {
return "TestTable [id=" + id + ", string=" + string + ", embedded=" + embedded + "]";
} }
Embedded.java
package sample.mybatis;
public class Embedded {
private int number;
@Override
public String toString() {
return "Embedded [number=" + number + "]";
} }
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.TestTable" id="testTableResultMap">
<id property="id" column="id" />
<result property="string" column="string" />
<!-- ★association タグで定義 -->
<association property="embedded" javaType="sample.mybatis.Embedded">
<result property="number" column="number" />
</association>
</resultMap>
<select id="selectTest" resultMap="testTableResultMap">
select * from test_table </select>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
session .selectList("sample.mybatis.selectTest")
.forEach(System.out::println);
} } } }
実行結果
TestTable [id=1, string=hoge, embedded=Embedded [number=100]] TestTable [id=2, string=fuga, embedded=Embedded [number=200]] TestTable [id=3, string=piyo, embedded=Embedded [number=300]] · <association> タグで、単一のクラスの埋め込みをマッピングできる。
· javaType 属性で、埋め込むクラスを指定する(エイリアス可)。
· 子要素で、埋め込みクラスのマッピングを定義する。
埋め込むクラスの定義を外出しする
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.TestTable" id="testTableResultMap">
<id property="id" column="id" />
<result property="string" column="string" />
<!-- ★resutlMap を指定 -->
<association property="embedded" resultMap="embeddedResultMap" />
</resultMap>
<!-- ★別途 resultMap で定義 -->
<resultMap type="sample.mybatis.Embedded" id="embeddedResultMap">
<result property="number" column="number" />
</resultMap>
<select id="selectTest" resultMap="testTableResultMap">
select * from test_table </select>
</mapper>
· <resultMap> で別途定義できる。
カラム名にプレフィックスを指定する
foo_table
bar_table
Foo.java
package sample.mybatis;
public class Foo {
private int id;
private Bar bar; @Override
public String toString() {
return "Foo [id=" + id + ", bar=" + bar + "]";
} }
Bar.java
package sample.mybatis;
public class Bar {
private int id;
@Override
public String toString() {
return "Bar [id=" + id + "]";
} }
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.Foo" id="fooResultMap">
<id property="id" column="id" />
<!-- ★columnPrefix で共通するプレフィックスを指定する -->
<association property="bar" columnPrefix="bar_" resultMap="barResultMap" />
</resultMap>
<resultMap type="sample.mybatis.Bar" id="barResultMap">
<id property="id" column="id" />
</resultMap>
<select id="selectFoo" resultMap="fooResultMap">
select foo.id ,bar.id bar_id -- ★"bar_" をプレフィックスとして付ける
from foo_table foo ,bar_table bar where bar.id = foo.bar_id order by foo.id asc </select>
</mapper>
selectFooの実行結果
Foo [id=1, bar=Bar [id=3]] Foo [id=2, bar=Bar [id=2]] Foo [id=3, bar=Bar [id=1]] · JOIN とかしたときに別テーブルの同名カラムと区別するために、よくプレフィックス(bar_)を付けると思う。
· そういうときでも resultMap を使い回すことができるよう、 columnPrefix という属性が用意されている。
· columnPrefix を指定すれば、 column の設定値にプレフィックスを付けた名称でマッピングが行われるようになる。
SELECT を分けて実行する
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.Foo" id="fooResultMap">
<id property="id" column="id" />
<association property="bar" column="bar_id" select="selectBar" />
</resultMap>
<select id="selectFoo" resultMap="fooResultMap">
select * from foo_table </select>
<select id="selectBar" resultType="sample.mybatis.Bar">
select * from bar_table where id = #{id} </select>
</mapper>
selectFooの実行結果
[DEBUG] s.m.selectFoo - ==> Preparing: select * from foo_table [DEBUG] s.m.selectFoo - ==> Parameters: [DEBUG] s.m.selectBar - ====> Preparing: select * from bar_table where id = ? [DEBUG] s.m.selectBar - ====> Parameters: 1(Integer) [DEBUG] s.m.selectBar - <==== Total: 1 [DEBUG] s.m.selectBar - ====> Preparing: select * from bar_table where id = ? [DEBUG] s.m.selectBar - ====> Parameters: 2(Integer) [DEBUG] s.m.selectBar - <==== Total: 1 [DEBUG] s.m.selectBar - ====> Preparing: select * from bar_table where id = ? [DEBUG] s.m.selectBar - ====> Parameters: 3(Integer) [DEBUG] s.m.selectBar - <==== Total: 1 [DEBUG] s.m.selectFoo - <== Total: 3 Foo [id=3, bar=Bar [id=1]] Foo [id=2, bar=Bar [id=2]] Foo [id=1, bar=Bar [id=3]] · foo_table を検索したあと、各列に対して bar_table の検索を別途実行している。
· <association> タグの select 属性で、 <select> タグのステートメントID を指定することで、こういう検索にできる。
· column 属性には、子テーブルの JOIN に使用するカラムを指定する。
JOIN キーが複数ある場合
foo_table
bar_table
Bar.java
package sample.mybatis;
public class Bar {
private int key1;
private String key2; @Override
public String toString() {
return "Bar [key1=" + key1 + ", key2=" + key2 + "]";
} }
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.Foo" id="fooResultMap">
<id property="id" column="id" />
<!-- ★キーが複数ある場合の指定方法 -->
<association property="bar" column="{key1=bar_key1,key2=bar_key2}" select="selectBar" />
</resultMap>
<select id="selectFoo" resultMap="fooResultMap">
select * from foo_table </select>
<select id="selectBar" resultType="sample.mybatis.Bar">
select * from bar_table where key1 = #{key1} and key2 = #{key2} </select>
</mapper>
selectFooの実行結果
[DEBUG] s.m.selectFoo - ==> Preparing: select * from foo_table [DEBUG] s.m.selectFoo - ==> Parameters: [DEBUG] s.m.selectBar - ====> Preparing: select * from bar_table where key1 = ? and key2 = ? [DEBUG] s.m.selectBar - ====> Parameters: 1(Integer), fuga(String) [DEBUG] s.m.selectBar - <==== Total: 1 [DEBUG] s.m.selectBar - ====> Preparing: select * from bar_table where key1 = ? and key2 = ? [DEBUG] s.m.selectBar - ====> Parameters: 1(Integer), hoge(String) [DEBUG] s.m.selectBar - <==== Total: 1 [DEBUG] s.m.selectBar - ====> Preparing: select * from bar_table where key1 = ? and key2 = ? [DEBUG] s.m.selectBar - ====> Parameters: 2(Integer), hoge(String) [DEBUG] s.m.selectBar - <==== Total: 1 [DEBUG] s.m.selectFoo - <== Total: 3 Foo [id=2, bar=Bar [key1=1, key2=fuga]] Foo [id=3, bar=Bar [key1=1, key2=hoge]] Foo [id=1, bar=Bar [key1=2, key2=hoge]] · JOIN するときのカラムが複数存在する場合は、 {prop1=column1,prop2=column2} という形で column属性を指定する。
関連するオブジェクトの読み込みを遅延させる
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.Foo" id="fooResultMap">
<id property="id" column="id" />
<association property="bar" column="{key1=bar_key1,key2=bar_key2}" select="selectBar"
fetchType="lazy" /> <!-- ★fetchType に lazy を指定 -->
</resultMap>
<select id="selectFoo" resultMap="fooResultMap">
select * from foo_table </select>
<select id="selectBar" resultType="sample.mybatis.Bar">
select * from bar_table where key1 = #{key1} and key2 = #{key2} </select>
</mapper>
Foo.java
package sample.mybatis;
public class Foo {
private int id;
public Bar bar; public void method() {
System.out.println("Foo.method() is called");
} }
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
System.out.println("@selectList()");
List<Foo> result = session.selectList("sample.mybatis.selectFoo");
System.out.println("@result.get(0)");
Foo foo = result.get(0);
System.out.println("** foo.class = " + foo.getClass() + " **");
System.out.println("@foo.bar 1");
System.out.println("<< foo.bar = " + foo.bar + " >>");
System.out.println("@foo.method()");
foo.method();
System.out.println("@foo.bar 2");
System.out.println("<< foo.bar = " + foo.bar + " >>");
} } } }
実行結果
@selectList() [DEBUG] s.m.selectFoo - ==> Preparing: select * from foo_table [DEBUG] s.m.selectFoo - ==> Parameters: [DEBUG] s.m.selectFoo - <== Total: 3 @result.get(0) ** foo.class = class sample.mybatis.Foo_$$_jvst80f_0 ** @foo.bar 1 << foo.bar = null >> @foo.method() [DEBUG] s.m.selectBar - ==> Preparing: select * from bar_table where key1 = ? and key2 = ? [DEBUG] s.m.selectBar - ==> Parameters: 1(Integer), fuga(String) [DEBUG] s.m.selectBar - <== Total: 1 Foo.method() is called @foo.bar 2 << foo.bar = Bar [key1=1, key2=fuga] >> · <association> の fetchType 属性に lazy とセットすると、関連するオブジェクトの読み込みを遅延させることができる。
· デフォルトは即時ロード(eager)。
· 遅延が設定されたクラスのインスタンスは、 MyBatis によって生成されたプロキシになる(class sample.mybatis.Foo_$$_jvst80f_0)。
· 遅延ロードでない場合は、普通のインスタンスが渡される。
· 遅延ロードが実行されるタイミングは、「プロキシのいずれかのメソッドが実行されたときの先頭」の模様。
· フィールドが読み取られたときではないようなので、メソッド実行前に参照すると null になっている(まぁ、直接参照することはないと思うけど、一応注意)。
全ての関連を遅延ロードさせたい場合は、ルートの設定で lazyLoadingEnabled を設定する方法がある。
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>
<settings>
<setting name="lazyLoadingEnabled" value="true"/> <!-- ★true にする -->
</settings>
... </configuration>
この場合でも、 <association> タグの fetchType の方が優先されるので、特定の読み込みだけ即時ロードにしたい場合は、設定を上書きできる。
コレクションをマッピングする
基本
foo_table
bar_table
Foo.java
package sample.mybatis;
import java.util.List;
public class Foo {
private int id;
private List<Bar> barList; @Override
public String toString() {
return "Foo [id=" + id + ", barList=" + barList + "]";
} }
Bar.java
package sample.mybatis;
public class Bar {
private int id;
@Override
public String toString() {
return "Bar [id=" + id + "]";
} }
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.Foo" id="fooResultMap">
<id property="id" column="id" />
<!-- ★collection タグを使用する -->
<collection property="barList" ofType="sample.mybatis.Bar">
<id property="id" column="bar_id" />
</collection>
</resultMap>
<select id="selectFoo" resultMap="fooResultMap">
select foo.id ,bar.id bar_id from foo_table foo ,bar_table bar where bar.foo_id = foo.id </select>
</mapper>
selectFooの実行結果
[DEBUG] s.m.selectFoo - ==> Preparing: select foo.id ,bar.id bar_id from foo_table foo ,bar_table bar where bar.foo_id = foo.id [DEBUG] s.m.selectFoo - ==> Parameters: [DEBUG] s.m.selectFoo - <== Total: 6 Foo [id=1, barList=[Bar [id=1], Bar [id=2], Bar [id=3]]] Foo [id=2, barList=[Bar [id=4], Bar [id=5]]] Foo [id=3, barList=[Bar [id=6]]] · コレクションをマッピングする場合は、 <collection> タグを使用する。
· 各要素の型は ofType 属性で指定する(エイリアス可)。
· それ以外は、 <association> タグのときと同じノリでいける。
SELECT を分割して実行する
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.Foo" id="fooResultMap">
<id property="id" column="id" />
<collection property="barList" column="id" select="selectBar" />
</resultMap>
<select id="selectFoo" resultMap="fooResultMap">
select * from foo_table </select>
<select id="selectBar" resultType="sample.mybatis.Bar">
select * from bar_table where foo_id = #{id} </select>
</mapper>
selectFooの実行結果
[DEBUG] s.m.selectFoo - ==> Preparing: select * from foo_table [DEBUG] s.m.selectFoo - ==> Parameters: [DEBUG] s.m.selectBar - ====> Preparing: select * from bar_table where foo_id = ? [DEBUG] s.m.selectBar - ====> Parameters: 1(Integer) [DEBUG] s.m.selectBar - <==== Total: 3 [DEBUG] s.m.selectBar - ====> Preparing: select * from bar_table where foo_id = ? [DEBUG] s.m.selectBar - ====> Parameters: 2(Integer) [DEBUG] s.m.selectBar - <==== Total: 2 [DEBUG] s.m.selectBar - ====> Preparing: select * from bar_table where foo_id = ? [DEBUG] s.m.selectBar - ====> Parameters: 3(Integer) [DEBUG] s.m.selectBar - <==== Total: 1 [DEBUG] s.m.selectFoo - <== Total: 3 Foo [id=1, barList=[Bar [id=1], Bar [id=2], Bar [id=3]]] Foo [id=2, barList=[Bar [id=4], Bar [id=5]]] Foo [id=3, barList=[Bar [id=6]]] · <association> のときと同じノリでできる。
· 遅延ロードも同じく可能。
条件によって異なるクラスにマッピングする
主に継承関係にあるクラスをマッピングするときに使用する方法。
base_table
Base.java
package sample.mybatis;
public class Base {
protected int id;
protected String commonValue; @Override
public String toString() {
return "Base [id=" + id + ", commonValue=" + commonValue + "]";
} }
Hoge.java
package sample.mybatis;
public class Hoge extends Base {
private String value; @Override
public String toString() {
return "Hoge [value=" + value + ", id=" + id + ", commonValue=" + commonValue + "]";
} }
Fuga.java
package sample.mybatis;
public class Fuga extends Base {
private String value; @Override
public String toString() {
return "Fuga [value=" + value + ", id=" + id + ", commonValue=" + commonValue + "]";
} }
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.Base" id="baseResultMap">
<id property="id" column="id" />
<result property="commonValue" column="common_value" />
<discriminator javaType="int" column="type">
<case value="1" resultType="sample.mybatis.Hoge">
<result property="value" column="hoge_value" />
</case>
<case value="2" resultType="sample.mybatis.Fuga">
<result property="value" column="fuga_value" />
</case>
</discriminator>
</resultMap>
<select id="selectBase" resultMap="baseResultMap">
select * from base_table </select>
</mapper>
selectBaseの実行結果
Hoge [value=hoge1, id=1, commonValue=common1] Fuga [value=fuga2, id=2, commonValue=common2] Base [id=3, commonValue=common3] · <discriminator> タグを使うことで、条件によって異なるクラスにマッピングさせることができるようになる。
· column 属性で、条件値を持つカラムを指定する。
· <case> タグで、条件値ごとにどういうマッピングを行うかを定義する。
· ここでは、 1 の場合は Hoge クラスに、 2 の場合は Fuga クラスにマッピングするように定義している。
· <case> 式に存在しない条件値の場合は、もともとの <resultMap> タグで指定していた type にマッピングされる。
ケースごとのマッピングを外出しする
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.Base" id="baseResultMap">
<id property="id" column="id" />
<result property="commonValue" column="common_value" />
<discriminator javaType="int" column="type">
<case value="1" resultMap="hogeResultMap" />
<case value="2" resultMap="fugaResultMap" />
</discriminator>
</resultMap>
<resultMap type="sample.mybatis.Hoge" id="hogeResultMap">
<result property="value" column="hoge_value" />
</resultMap>
<resultMap type="sample.mybatis.Fuga" id="fugaResultMap">
<result property="value" column="fuga_value" />
<result property="commonValue" column="common_value" />
</resultMap>
<select id="selectBase" resultMap="baseResultMap">
select * from base_table </select>
</mapper>
実行結果
Hoge [value=hoge1, id=1, commonValue=null] Fuga [value=fuga2, id=2, commonValue=common2] Base [id=3, commonValue=common3] · <case> ごとの定義は、 <resultMap> で抽出できる。
· ただし、その場合共通部分で定義していたカラムは <id> 以外はマッピングされないので、個々の <resultMap> で定義しないといけない。
· hogeResultMap は、 commonValue を定義していないので null になっている。
· いちいち個々の <resultMap> で共通項目を定義しなおすのがシンドイ場合は、 extends 属性を使う方法が用意されている。
extends 属性で共通部分を継承する
sample_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="sample.mybatis">
<resultMap type="sample.mybatis.Base" id="baseResultMap">
<id property="id" column="id" />
<result property="commonValue" column="common_value" />
<discriminator javaType="int" column="type">
<case value="1" resultMap="hogeResultMap" />
<case value="2" resultMap="fugaResultMap" />
</discriminator>
</resultMap>
<!-- ★extends で baseResultMap を継承 -->
<resultMap type="sample.mybatis.Hoge" id="hogeResultMap" extends="baseResultMap">
<result property="value" column="hoge_value" />
</resultMap>
<resultMap type="sample.mybatis.Fuga" id="fugaResultMap" extends="baseResultMap">
<result property="value" column="fuga_value" />
</resultMap>
<select id="selectBase" resultMap="baseResultMap">
select * from base_table </select>
</mapper>
実行結果
Hoge [value=hoge1, id=1, commonValue=common1] Fuga [value=fuga2, id=2, commonValue=common2] Base [id=3, commonValue=common3] キャッシュ
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();) {
TestTable testTable = selectAndPrintln("session1", session1);
testTable.setValue("update");
selectAndPrintln("session1", session1);
selectAndPrintln("session2", session2);
} } } private static TestTable selectAndPrintln(String tag, SqlSession session) {
TestTable result = session.selectOne("sample.mybatis.selectTest");
System.out.printf("<<%s>> %s%n", tag, result);
return result; } }
実行結果
[DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where id=1 [DEBUG] s.m.selectTest - ==> Parameters: [DEBUG] s.m.selectTest - <== Total: 1 <<session1>> TestTable [id=1, value=hoge] <<session1>> TestTable [id=1, value=update] [DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where id=1 [DEBUG] s.m.selectTest - ==> Parameters: [DEBUG] s.m.selectTest - <== Total: 1 <<session2>> TestTable [id=1, value=hoge] · 検索結果はキャッシュされる。
· 同じ検索を再度実行すると、キャッシュされているオブジェクトが返される。
· キャッシュはセッションごとに保持されるため、他のセッションでの変更の影響は受けない。
· デフォルトは、以下のような動作をするらしい。
· 1024 個のオブジェクトをキャッシュする。
· 最も使われていないキャッシュから削除されていく。
· 時間経過では削除されない。
· insert, update, delete が実行されるとキャッシュはクリアされる。
· このデフォルトの動作は設定ファイルで上書きすることができる。
· 設定値の詳細は こちら を参照。
更新系処理
登録
基本
sample_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="sample.mybatis">
<insert id="insertTest">
insert into test_table ( value ) values ( #{value} ) </insert>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
session.insert("sample.mybatis.insertTest", "hoge");
session.commit();
} } } }
実行結果
[DEBUG] s.m.insertTest - ==> Preparing: insert into test_table ( value ) values ( ? ) [DEBUG] s.m.insertTest - ==> Parameters: hoge(String) [DEBUG] s.m.insertTest - <== Updates: 1 test_table
· <insert> タグで INSERT 文を定義できる。
· だいたい <select> のときと同じノリで定義できる。
オブジェクトをパラメータとして渡す
TestTable.java
package sample.mybatis;
public class TestTable {
private int id;
private String value; public TestTable(String value) {
this.value = value;
} @Override
public String toString() {
return "TestTable [id=" + id + ", value=" + value + "]";
} }
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
TestTable table = new TestTable("fuga");
session.insert("sample.mybatis.insertTest", table);
session.commit();
} } } }
実行結果
[DEBUG] s.m.insertTest - ==> Preparing: insert into test_table ( value ) values ( ? ) [DEBUG] s.m.insertTest - ==> Parameters: fuga(String) [DEBUG] s.m.insertTest - <== Updates: 1 · こちらも <select> のときと同じ感じ。
· parameterType は省略可能。
自動生成されたキーを取得する
sample_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="sample.mybatis">
<insert id="insertTest" useGeneratedKeys="true" keyProperty="id">
insert into test_table ( value ) values ( #{value} ) </insert>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
TestTable table = new TestTable("piyo");
session.insert("sample.mybatis.insertTest", table);
session.commit();
System.out.println(table);
} } } }
実行結果
[DEBUG] s.m.insertTest - ==> Preparing: insert into test_table ( value ) values ( ? ) [DEBUG] s.m.insertTest - ==> Parameters: fuga(String) [DEBUG] s.m.insertTest - <== Updates: 1 TestTable [id=7, value=piyo] · <insert> タグに useGeneratedKeys と keyProperty を指定する。
· useGeneratedKeys は true を指定。
· keyProperty にはキー値を設定する Java 側のフィールド(プロパティ)名を指定する。
· すると、 INSERT 時にデータベースによって生成された ID が自動で Java のインスタンスにセットされるようになる。
Oracle のシーケンスオブジェクトの場合
ID をシーケンスオブジェクトから取得する場合は、以下のようにする。
sample_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="sample.mybatis">
<insert id="insertTest">
<selectKey keyProperty="id" resultType="_int" order="BEFORE">
select test_table_seq.nextval from dual </selectKey>
insert into test_table ( id ,value ) values ( #{id} ,#{value} ) </insert>
</mapper>
実行結果
[DEBUG] s.m.insertTest!selectKey - ==> Preparing: select test_table_seq.nextval from dual [DEBUG] s.m.insertTest!selectKey - ==> Parameters: [DEBUG] s.m.insertTest!selectKey - <== Total: 1 [DEBUG] s.m.insertTest - ==> Preparing: insert into test_table ( id ,value ) values ( ? ,? ) [DEBUG] s.m.insertTest - ==> Parameters: 11(Integer), aaa(String) [DEBUG] s.m.insertTest - <== Updates: 1 TestTable [id=11, value=aaa] 確認
SQL> select * from test_table; ID VALUE ---------- ----- 11 aaa · <insert> タグの中に <selectKey> タグを宣言する。
· keyProperty 属性で、生成された ID をセットする Java フィールド(プロパティ)名を指定する。
· order 属性に BEFORE をセットすることで、まずシーケンスから値を取得してから INSERT が実行されるようになる。
更新
test_table
TestTable.java
package sample.mybatis;
public class TestTable {
private int id;
private String value; public TestTable(int id, String value) {
this.id = id;
this.value = value;
} }
sample_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="sample.mybatis">
<update id="updateTest">
update test_table set value = #{value} where id = #{id} </update>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
TestTable table = new TestTable(1, "update");
session.insert("sample.mybatis.updateTest", table);
session.commit();
} } } }
実行結果
[DEBUG] s.m.updateTest - ==> Preparing: update test_table set value = ? where id = ? [DEBUG] s.m.updateTest - ==> Parameters: update(String), 1(Integer) [DEBUG] s.m.updateTest - <== Updates: 1 · <update> タグで UPDATE を定義できる。
削除
test_table
TestTable.java
package sample.mybatis;
public class TestTable {
private int id;
private String value; public TestTable(int id) {
this.id = id;
} }
sample_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="sample.mybatis">
<delete id="deleteTest">
delete from test_table where id = #{id} </delete>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
TestTable table = new TestTable(2);
session.insert("sample.mybatis.deleteTest", table);
session.commit();
} } } }
実行結果
[DEBUG] s.m.deleteTest - ==> Preparing: delete from test_table where id = ? [DEBUG] s.m.deleteTest - ==> Parameters: 2(Integer) [DEBUG] s.m.deleteTest - <== Updates: 1 · <delete> タグで DELETE 文を定義できる。
バッチ更新
デフォルトでは、 SQL を実行するたびに PreparedStatement を生成して SQL をデータベースに対して発行している。
更新件数が多くなる場合、これは非常に効率が悪い。
MyBatis では、設定により PreparedStatement の使い回しや、バッチ更新ができるようになっている。
sample_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="sample.mybatis">
<insert id="insertTest">
insert into test_table (value) values (#{value}) </insert>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
for (int i=0; i<3; i++) {
TestTable t = new TestTable("value-" + i);
int updateCount = session.insert("sample.mybatis.insertTest", t);
System.out.println("updateCount = " + updateCount);
} session.commit();
} } } }
· 3回ループして、 INSERT を実行する実装。
デフォルトの動作
実行結果
[DEBUG] s.m.insertTest - ==> Preparing: insert into test_table (value) values (?) [DEBUG] s.m.insertTest - ==> Parameters: value-0(String) [DEBUG] s.m.insertTest - <== Updates: 1 updateCount = 1 [DEBUG] s.m.insertTest - ==> Preparing: insert into test_table (value) values (?) [DEBUG] s.m.insertTest - ==> Parameters: value-1(String) [DEBUG] s.m.insertTest - <== Updates: 1 updateCount = 1 [DEBUG] s.m.insertTest - ==> Preparing: insert into test_table (value) values (?) [DEBUG] s.m.insertTest - ==> Parameters: value-2(String) [DEBUG] s.m.insertTest - <== Updates: 1 updateCount = 1 · 毎回 PreparedStatement が生成されて、 SQL が発行されている。
PreparedStatement を再利用する
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>
<settings>
<setting name="defaultExecutorType" value="REUSE"/>
</settings>
... </configuration>
実行結果
[DEBUG] s.m.insertTest - ==> Preparing: insert into test_table (value) values (?) [DEBUG] s.m.insertTest - ==> Parameters: value-0(String) [DEBUG] s.m.insertTest - <== Updates: 1 updateCount = 1 [DEBUG] s.m.insertTest - ==> Parameters: value-1(String) [DEBUG] s.m.insertTest - <== Updates: 1 updateCount = 1 [DEBUG] s.m.insertTest - ==> Parameters: value-2(String) [DEBUG] s.m.insertTest - <== Updates: 1 updateCount = 1 · 設定ファイルで defaultExecutorType に REUSE を設定する。
· PreparedStatement の生成は1回だけになる。
· SQL の発行は毎回行われている。
バッチ更新にする
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>
<settings>
<setting name="defaultExecutorType" value="BATCH"/>
</settings>
... </configuration>
実行結果
[DEBUG] s.m.insertTest - ==> Preparing: insert into test_table (value) values (?) [DEBUG] s.m.insertTest - ==> Parameters: value-0(String) updateCount = -2147482646 [DEBUG] s.m.insertTest - ==> Parameters: value-1(String) updateCount = -2147482646 [DEBUG] s.m.insertTest - ==> Parameters: value-2(String) updateCount = -2147482646 · defaultExecutorType を BATCH にするとバッチ更新になる。
· SQL の発行は最後にまとめて行われる。
· なので、更新件数は取得できなくなっている。
SqlSession を取得するときに指定する
バッチ更新にするかどうかの設定は、 SqlSession を取得するときの引数でも指定できる。
import org.apache.ibatis.session.ExecutorType;
...
SqlSession session = factory.openSession(ExecutorType.BATCH);
· openSession() の引数に ExecutorType を渡すことで指定できる。
· ここで指定した条件は、設定ファイルよりも優先される。
Mapper
ここまでは、 SqlSession に直接ステートメント ID とパラメータを渡す方法で SQL を実行していた。
MyBatis には、これとは別に Mapper という仕組みも用意されている。
Mapper を使うと、 SqlSession を直接使うより型安全になる。
基本的にこの Mapper を使用した方法の方が推奨っぽい。
(マッピングのファイルが自然と Mapper 単位になるので、管理もしやすくなるっぽい)
基本
test_table
TestTable.java
package sample.mybatis;
public class TestTable {
private int id;
private String string; private int number;
@Override
public String toString() {
return "TestTable [id=" + id + ", string=" + string + ", number=" + number + "]";
} }
sample_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="sample.mybatis.TestTableMapper"> <!-- ★Mapper の FQCN を namespace にする -->
<select id="selectAll" resultType="sample.mybatis.TestTable">
select * from test_table </select>
</mapper>
TestTableMapper.java
package sample.mybatis;
import java.util.List;
// ★ Mapper インターフェース
public interface TestTableMapper {
// ★ステートメントID と同じ名前のメソッドを定義する
List<TestTable> selectAll();
}
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
// ★Mapper のインスタンスを取得する
TestTableMapper mapper = session.getMapper(TestTableMapper.class);
mapper.selectAll().forEach(System.out::println);
} } } }
実行結果
[DEBUG] s.m.T.selectAll - ==> Preparing: select * from test_table [DEBUG] s.m.T.selectAll - ==> Parameters: [DEBUG] s.m.T.selectAll - <== Total: 3 TestTable [id=1, string=hoge, number=100] TestTable [id=2, string=fuga, number=200] TestTable [id=3, string=piyo, number=300] · Mapper を利用する場合、専用のインターフェースを定義する(TestTableMapper)。
· このインターフェースのメソッドと、設定ファイルの各ステートメントが対応するようにそれぞれを定義する。
· ステートメントの方は、 namespace をインターフェースの FQCN にする(sample.mybatis.TestTableMapper)。
· ステートメントID とインターフェースのメソッド名を一致させる(selectAll)。
· パラメータが存在する場合は、メソッドに引数を渡すように定義する。
· Mapper のインスタンスは、 SqlSession の getMapper() メソッドで取得する。
パラメータを複数の引数に分けて渡す
sample_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="sample.mybatis.TestTableMapper">
<select id="selectByStringOrNumber" resultType="sample.mybatis.TestTable">
select * from test_table where string = #{string} or number = #{number} </select>
</mapper>
TestTableMapper.java
package sample.mybatis;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface TestTableMapper {
List<TestTable> selectByStringOrNumber(
@Param("string") String string,
@Param("number") int number
); }
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
TestTableMapper mapper = session.getMapper(TestTableMapper.class);
mapper.selectByStringOrNumber("hoge", 300).forEach(System.out::println);
} } } }
実行結果
[DEBUG] s.m.T.selectByStringOrNumber - ==> Preparing: select * from test_table where string = ? or number = ? [DEBUG] s.m.T.selectByStringOrNumber - ==> Parameters: hoge(String), 300(Integer) [DEBUG] s.m.T.selectByStringOrNumber - <== Total: 2 TestTable [id=1, string=hoge, number=100] TestTable [id=3, string=piyo, number=300] · パラメータが複数存在するケースで、メソッドの引数をそれぞれ分けて定義したい場合は @Paramアノテーションで引数をアノテートする。
· @Param の value には、パラメータの名前を指定する。
動的 SQL
条件に合わせて SQL を動的に組み立てることができる。
if
test_table
sample_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="sample.mybatis">
<select id="selectTest" resultType="sample.mybatis.TestTable">
select * from test_table where string_value = 'hoge' <if test="numberValue != null"> <!-- ★if タグで条件分岐 -->
and number_value = #{numberValue} </if>
</select>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
session .selectList("sample.mybatis.selectTest") // ★パラメータ未設定
.forEach(System.out::println);
Map<String, Object> param = new HashMap<>(); param.put("numberValue", 100);
session .selectList("sample.mybatis.selectTest", param) // ★パラメータを設定
.forEach(System.out::println);
} } } }
実行結果
[DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where string_value = 'hoge' [DEBUG] s.m.selectTest - ==> Parameters: [DEBUG] s.m.selectTest - <== Total: 2 TestTable [id=1, stringValue=hoge, numberValue=100] TestTable [id=2, stringValue=hoge, numberValue=200] [DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where string_value = 'hoge' and number_value = ? [DEBUG] s.m.selectTest - ==> Parameters: 100(Integer) [DEBUG] s.m.selectTest - <== Total: 1 TestTable [id=1, stringValue=hoge, numberValue=100] · <if> タグを使用することで、条件が満たされたときだけ SQL を追加させたりできるようになる。
· test 属性で条件式を記述する。
· この中では、検索条件で渡すパラメータの値を参照できる。
· AND や OR 条件を記述するときは、 and, or を使用する(&&, || ではない!)。
choose
test_table
sample_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="sample.mybatis">
<select id="selectTest" resultType="sample.mybatis.TestTable">
select * from test_table <choose>
<when test="value == null">
where value is null </when>
<otherwise>
where value = #{value} </otherwise>
</choose>
</select>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
TestTable testTable = session.selectOne("sample.mybatis.selectTest");
System.out.println(testTable);
Map<String, Object> param = new HashMap<>(); param.put("value", "hoge");
testTable = session.selectOne("sample.mybatis.selectTest", param);
System.out.println(testTable);
} } } }
実行結果
[DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where value is null [DEBUG] s.m.selectTest - ==> Parameters: [DEBUG] s.m.selectTest - <== Total: 1 TestTable [id=2, value=null] [DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where value = ? [DEBUG] s.m.selectTest - ==> Parameters: hoge(String) [DEBUG] s.m.selectTest - <== Total: 1 TestTable [id=1, value=hoge] · <choose> タグを使うことで、複数の選択肢のうち1つだけを適用するという条件を定義できる。
· <when> タグで、指定した条件が満たされた場合の SQL を記述する。
· <otherwise> タグで、それ以外の条件が全て満たされなかった場合の SQL を記述する。
where
test_table
sample_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="sample.mybatis">
<select id="selectTest" resultType="sample.mybatis.TestTable">
select * from test_table <where>
<if test="string != null">
and string = #{string} </if>
<if test="number != null">
and number = #{number} </if>
</where>
</select>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
// ★パラメータを空で実行
Map<String, Object> param = new HashMap<>(); selectAndPrint(session, param); // ★パラメータに string だけ指定して実行
param = new HashMap<>(); param.put("string", "hoge");
selectAndPrint(session, param); // ★パラメータに string と number を指定して実行
param = new HashMap<>(); param.put("string", "hoge");
param.put("number", 200);
selectAndPrint(session, param); } } } private static void selectAndPrint(SqlSession session, Map<String, Object> param) {
session .selectList("sample.mybatis.selectTest", param)
.forEach(System.out::println);
System.out.println();
} }
実行結果
[DEBUG] s.m.selectTest - ==> Preparing: select * from test_table [DEBUG] s.m.selectTest - ==> Parameters: [DEBUG] s.m.selectTest - <== Total: 4 TestTable [id=1, string=hoge, number=100] TestTable [id=2, string=hoge, number=200] TestTable [id=3, string=fuga, number=200] TestTable [id=4, string=piyo, number=300] [DEBUG] s.m.selectTest - ==> Preparing: select * from test_table WHERE string = ? [DEBUG] s.m.selectTest - ==> Parameters: hoge(String) [DEBUG] s.m.selectTest - <== Total: 2 TestTable [id=1, string=hoge, number=100] TestTable [id=2, string=hoge, number=200] [DEBUG] s.m.selectTest - ==> Preparing: select * from test_table WHERE string = ? and number = ? [DEBUG] s.m.selectTest - ==> Parameters: hoge(String), 200(Integer) [DEBUG] s.m.selectTest - <== Total: 1 TestTable [id=2, string=hoge, number=200] · <where> タグを使うと、子要素が何らかの文字列が存在した場合に限り WHERE 句を先頭に追加してくれるようになる。
· また、 <where> タグ内の文字列が AND か OR で始まる場合、自動でその記述が除去される。
· この動きを <if> などだけで実現使用とすると、結構複雑な記述になってしまう。
trim タグで置き換える
上記の <where> を使った定義は、 <trim> タグを使って以下のように置き換えることができる。
sample_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="sample.mybatis">
<select id="selectTest" resultType="sample.mybatis.TestTable">
select * from test_table <trim prefix="where" prefixOverrides="AND|OR">
<if test="string != null">
and string = #{string} </if>
<if test="number != null">
and number = #{number} </if>
</trim>
</select>
</mapper>
· prefix 属性に、先頭に追加する文字列。
· prefixOverrides 属性に、先頭から除去する文字列をパイプ(|)区切りで指定する。
set
test_table
TestTable.java
package sample.mybatis;
public class TestTable {
private int id;
private String string; private Integer number; public TestTable id(int id) {
this.id = id;
return this; } public TestTable string(String string) {
this.string = string;
return this; } public TestTable number(int number) {
this.number = number;
return this; } }
sample_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="sample.mybatis">
<update id="updateTest">
update test_table <set>
<if test="string != null">
string = #{string}, </if>
<if test="number != null">
number = #{number}, </if>
</set>
where id = #{id} </update>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
session.update("sample.mybatis.updateTest",
new TestTable().id(1).number(555));
session.update("sample.mybatis.updateTest",
new TestTable().id(3).string("update").number(999));
session.commit();
} } } }
実行結果
[DEBUG] s.m.updateTest - ==> Preparing: update test_table SET number = ? where id = ? [DEBUG] s.m.updateTest - ==> Parameters: 555(Integer), 1(Integer) [DEBUG] s.m.updateTest - <== Updates: 1 [DEBUG] s.m.updateTest - ==> Preparing: update test_table SET string = ?, number = ? where id = ? [DEBUG] s.m.updateTest - ==> Parameters: update(String), 999(Integer), 3(Integer) [DEBUG] s.m.updateTest - <== Updates: 1 test_table
· <set> タグを使うと、内部の文字列が空でない場合に、先頭に SET 句が追加される。
· また、末尾のカンマ(,)が自動で除去される。
trim タグで置き換える
<where> のときと同じように、 <trim> で置き換えることもできる。
sample_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="sample.mybatis">
<update id="updateTest">
update test_table <trim prefix="set" suffixOverrides=",">
<if test="string != null">
string = #{string}, </if>
<if test="number != null">
number = #{number}, </if>
</trim>
where id = #{id} </update>
</mapper>
· suffixOverrides 属性で、末尾の削除する文字を指定する。
foreach
test_table
sample_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="sample.mybatis">
<select id="selectTest" resultType="sample.mybatis.TestTable">
select * from test_table where id in <foreach collection="list" item="item"
open="(" separator="," close=")">
#{item} </foreach>
</select>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import java.util.Arrays;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
session .selectList("sample.mybatis.selectTest", Arrays.asList(1, 3, 5))
.forEach(System.out::println);
} } } }
実行結果
[DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where id in ( ? , ? , ? ) [DEBUG] s.m.selectTest - ==> Parameters: 1(Integer), 3(Integer), 5(Integer) [DEBUG] s.m.selectTest - <== Total: 3 TestTable [id=1] TestTable [id=3] TestTable [id=5] · <foreach> タグを使用すると、コレクションを反復処理しながら SQL を構築できる。
· collection 属性に、反復処理するコレクションを指定する。
· list は、パラメータに反復処理可能なオブジェクトを渡したときにデフォルトで利用できる参照名(特殊なパラメータ名 を参照)。
· item 属性で、反復中の各要素を参照するための一時変数名を宣言する。
· open 属性は、反復の先頭に追加する文字列を指定する。
· separator 属性は、各反復結果の間に挿入する文字列を指定する。
· close 属性は、反復の最後に追加する文字列を指定する。
· index 属性というのもあり、反復中のインデックス値を参照するための一時変数名を宣言できる。
· おもに IN 句の動的生成に利用する。
bind
sample_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="sample.mybatis">
<select id="selectTest" resultType="sample.mybatis.TestTable">
<bind name="parameter" value="'@@@' + _parameter + '@@@'"/>
select * from test_table where value = #{parameter} </select>
</mapper>
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
session.selectList("sample.mybatis.selectTest", "abc");
} } } }
実行結果
[DEBUG] s.m.selectTest - ==> Preparing: select * from test_table where value = ? [DEBUG] s.m.selectTest - ==> Parameters: @@@abc@@@(String) · <bind> タグで、一時変数を定義できる。
· name 属性で、一時変数の名前を定義する。
· value 属性で、 OGNL 式を使った値の定義ができる。
· _parameter は、パラメータとして渡した値を参照できる暗黙変数(特殊なパラメータ名 を参照)。
プログラムで SQL を動的生成する
MyBatis にはアノテーションを使って SQL を定義する方法が用意されている。
しかし、文字列連結で SQL を組み立てていると、カンマ区切りとか AND, OR 区切りなど、注意しないといけない細かい問題がいろいろある。
そのへんの面倒な部分を隠蔽した API が用意されている。
sample_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="sample.mybatis.TestTableMapper"> <!-- ★Mapper を namespace に指定 -->
</mapper>
TestTableMapper.java
package sample.mybatis;
import java.util.List;
import org.apache.ibatis.annotations.SelectProvider;
// ★Mapper インターフェースをつくる
public interface TestTableMapper {
// ★@SelectProvider で SQL を生成するクラスとメソッドを設定する
@SelectProvider(type=TestTableSqlProvider.class, method="getSelectTestSql")
List<TestTable> selectTest();
}
TestTableSqlProvider.java
package sample.mybatis;
import org.apache.ibatis.jdbc.SQL;
// ★SQL を生成するクラス。
public class TestTableSqlProvider {
// ★SQL を生成するメソッド
public String getSelectTestSql() {
// ★SQL クラスを使って、 SQL を生成する
SQL sql = new SQL() {{ // ★匿名クラスで生成
SELECT("id, hoge_value");
SELECT("fuga_value");
SELECT("piyo_value");
FROM("test_table");
}}; return sql.toString();
} }
Main.java
package sample.mybatis;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession session = factory.openSession()) {
TestTableMapper mapper = session.getMapper(TestTableMapper.class);
mapper.selectTest();
} } } }
実行結果
[DEBUG] s.m.T.selectTest - ==> Preparing: SELECT id, hoge_value, fuga_value, piyo_value FROM test_table WHERE (hoge_value is not null) [DEBUG] s.m.T.selectTest - ==> Parameters: · Mapper のメソッドを @SelectProvider でアノテートする。
· type に SQL を生成するクラスを、 method に SQL を生成するメソッドの名前を指定する。
· INSERT や UPDATE には @InsertProvider や @UpdateProvider がある。
· SQL の生成には、 org.apache.ibatis.jdbc.SQL クラスを使用する。
· SQL クラスを匿名クラスとして new して、インスタンス初期化ブロック内で SELECT() や FROM()メソッドを使って SQL を構築する。
· 各引数には文字列で SQL の部品を渡す。
· この際、カンマ区切りなどは必要に応じて追加されたりする。
· 生成した SQL インスタンスの toString() メソッドで構築した SQL を文字列として取得できる。
各メソッドの使い方について
SELECT
new SQL() {{
SELECT("foo");
SELECT("bar");
SELECT("fizz, buzz");
FROM("foo_table");
}};
SELECT foo, bar, fizz, buzz FROM foo_table
· 連続して記述することで、各項目がカンマで勝手に区切られる。
· 明示的にカンマ区切りの文字列を渡してもOK。
· 末尾に不要なカンマがある場合は、除去してくれない。
FROM
new SQL() {{
SELECT("*");
FROM("foo_table");
FROM("bar_table");
}};
SELECT * FROM foo_table, bar_table
· FROM を連続させることで、カンマ区切りで FROM 句が列挙される。
WHERE
new SQL() {{
SELECT("*");
FROM("foo_table");
WHERE("foo = ?");
WHERE("bar = ?");
AND(); WHERE("fizz = ?");
WHERE("buzz = ?");
OR(); WHERE("hoge = ?");
WHERE("fuga = ?");
}};
SELECT *
FROM foo_table WHERE ( foo = ? AND bar = ? ) AND ( fizz = ? AND buzz = ? ) OR ( hoge = ? AND fuga = ? ) · WHERE() メソッドで WHERE 句を生成できる。
· 連続させると AND 条件で連結される。
· AND() および OR() メソッドで AND, OR で連結できる。
INNER_JOIN
new SQL() {{
SELECT("*");
FROM("foo_table");
INNER_JOIN("bar_table on bar_table.id = foo_table.bar_id");
INNER_JOIN("fizz_table on fizz_table.id = bar_table.fizz_id");
}};
SELECT * FROM foo_table INNER JOIN bar_table
on bar_table.id = foo_table.bar_id INNER JOIN fizz_table
on fizz_table.id = bar_table.fizz_id · INNER_JOIN() メソッドで、 INNER JOIN 句を追加できる。
· 結合条件は頑張って文字列で組み立てないといけないっぽい。
ORDER_BY
new SQL() {{
SELECT("*");
FROM("foo_table");
ORDER_BY("foo");
ORDER_BY("bar desc");
}};
SELECT * FROM foo_table ORDER BY foo, bar desc
· ORDER_BY() メソッドで ORDER BY 句を生成できる。
· 連続させることで、自動でカンマ区切りになる。
GROUP_BY
new SQL() {{
SELECT("foo, bar");
FROM("foo_table");
GROUP_BY("foo");
GROUP_BY("bar");
}};
SELECT foo, bar FROM foo_table GROUP BY foo, bar
· GROUP_BY() メソッドで、 GROUP BY 句を生成できる。
· 連続させることで、自動でカンマ区切りになる。
HAVING
new SQL() {{
SELECT("foo, count(*)");
FROM("foo_table");
GROUP_BY("foo");
HAVING("0 < count(*)");
HAVING("count(*) < 100");
}};
SELECT foo ,count(*) FROM foo_table GROUP BY foo
HAVING ( 0 < count(foo)
AND count(foo) < 100
) · HAVING() メソッドで HAVING 句を生成できる。
· WHERE() と同じような動作をする(連続させると AND で連結される)。
INSERT_INTO, VALUES
new SQL() {{
INSERT_INTO("foo_table");
VALUES("foo_value", "#{foo_value}");
VALUES("fizz_value, buzz_value", "#{fizz_value}, #{buzz_value}");
}};
INSERT INTO foo_table (foo_value, fizz_value, buzz_value) VALUES (?, ?, ?)
· INSERT_INTO() メソッドで INSERT 文を生成できる。
· VALUES() メソッドで、登録する項目を定義できる。
· 第一引数に値を設定する項目の名前を、
· 第二引数に VALUES 句で指定する値を渡す。
· 複数記述すると、自動でカンマ区切りになる。
UPDATE, SET
new SQL() {{
UPDATE("foo_table");
SET("foo_value = #{foo_value}");
SET("bar_value = #{bar_value}");
WHERE("id = #{id}");
}};
UPDATE foo_table SET foo_value = ?, bar_value = ? WHERE (id = ?)
· UPDATE() メソッドで、 UPDATE 文を生成できる。
· SET() メソッドで、 SET 句を生成できる。
· 連続させることで、自動でカンマ区切りになる。
DELETE_FROM
new SQL() {{
DELETE_FROM("foo_table");
WHERE("id = #{id}");
}};
DELETE FROM foo_table WHERE (id = ?)
· DELETE_FROM() メソッドで DELETE 文を生成できる。
Spring Boot と連携する
Spring Boot 用のプラグインが用意されているので、それを使えば簡単に連携できる。
test_table
フォルダ構成
|-build.gradle `-src/main/ |-java/ | `-sample/mybatis/springboot/ | |-Main.java | `-TestTableMapper.java `-resources/ |-application.properties |-mybatis-config.xml `-TestTableMapper.xml build.gradle
buildscript {
repositories { mavenCentral() } dependencies { classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.3.1.RELEASE'
} }
apply plugin: 'java'
apply plugin: 'spring-boot'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
compileJava.options.encoding = 'UTF-8'
repositories {
mavenCentral() }
dependencies {
compile 'org.springframework.boot:spring-boot-starter'
// ★MyBatis の SpringBoot 用プラグインを読み込む
compile 'org.mybatis.spring.boot:mybatis-spring-boot-starter:1.0.0'
compile 'mysql:mysql-connector-java:5.1.38'
}
application.properties
# ★MyBatis の設定ファイルを読み込む
mybatis.config=mybatis-config.xml
# ★データソースの設定
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=test
spring.datasource.password=test
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
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>
<!-- ★データソースの管理は Spring Boot に任せるので、こちらには記述しない -->
<mappers>
<mapper resource="TestTableMapper.xml"/>
</mappers>
</configuration>
TestTableMapper.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="sample.mybatis.springboot.TestTableMapper">
<select id="selectTest" resultType="map">
select * from test_table </select>
</mapper>
TestTableMapper.java
package sample.mybatis.springboot;
import java.util.List;
import java.util.Map;
public interface TestTableMapper {
List<Map<String, Object>> selectTest();
}
Main.java
package sample.mybatis.springboot;
import java.io.IOException;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Main {
public static void main(String[] args) throws IOException {
try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
Main m = ctx.getBean(Main.class);
m.method();
} } @Autowired
private SqlSessionTemplate sqlSession; @Autowired
private TestTableMapper mapper; public void method() throws IOException {
System.out.println("[SqlSessionTemplate]");
this.sqlSession
.selectList("sample.mybatis.springboot.TestTableMapper.selectTest")
.forEach(System.out::println);
System.out.println("[TestTableMapper]");
this.mapper
.selectTest()
.forEach(System.out::println);
} }
実行結果
[SqlSessionTemplate] {id=1, value=hoge} {id=2, value=fuga} {id=3, value=piyo} [TestTableMapper] {id=1, value=hoge} {id=2, value=fuga} {id=3, value=piyo} · Spring Boot で MyBatis を使う場合は、 mybatis-spring-boot-starter を依存関係に追加する。
· 次に Spring Boot の設定ファイル(application.properties)にて、 mybatis.config という名前で MyBatis の設定ファイルを指定する。
· データソースは Spring Boot に管理してもらうので、 Spring Boot の方に設定を記述して、 MyBatis の方には記述しない。
· SqlSession にアクセスするには、 SqlSessionTemplate を @Autowired でインジェクションすればいい。
· SqlSessionTemplate の API は SqlSession と同じ。
· SqlSession と違いスレッドセーフで、異なるスレッドや DAO から使っても問題ない。
· Mapper インターフェースを定義しておけば、 @Autowired でインジェクションできる。
· Spring にスキャンされる場所にインターフェースを入れておく必要がある。
· Main クラスを @MapperScan でアノテートすれば、任意のパッケージの Mapper インターフェースをスキャンすることもできる(例:@MapperScan("foo.bar"))。
· こちらもスレッドセーフなインスタンスがインジェクションされるため、異なるスレッドや DAO から利用しても問題ない。
参考
· MyBatis – MyBatis 3 | イントロダクション
· mybatis-spring – MyBatis-Spring | イントロダクション
· MyBatisでダイナミックSQL(動的SQL)をアノテーションでやる|Pimp my Code. @wataru420
0 件のコメント:
コメントを投稿