値オブジェクトとMyBatisの組み合わせでハマったポイントをメインに構文とかメモとかを書いておく。ハマったと言いつつどうハマったのかは書いてません。
雛型
上2行はおまじない。
namespace
に、xmlに対応するMapper.javaの絶対パスを記入する。
<?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="">
</mapper>
package com.example.demo.infrastructure
@Mapper
public interface Mapper{
}
<?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.demo.infrastructure.Mapper"> <-Mapper.javaの絶対パス
</mapper>
便利設定
通常は、マッパーインターフェースが配置されているフォルダ構造と同じようにMapper.xmlを配置する必要がある。
application.propertiesにこのように記述するとプロジェクト配下の全てのフォルダを対象にMapper.xmlを探す。
メリット:HogeMapper.java
とHogeMapper.xml
を同じフォルダに配置できる。
mybatis.mapper-locations:classpath:project_name/**/*.xml
CRUD
タグ内でCRUDするためのSQLを書く時は下記のようにする。
タグ | 説明 |
---|---|
id | SQLに対する識別子。Mapper.javaのメソッド名と対応させる |
resultType | 戻り値のデータ型。プリミティブ型、オブジェクト型どちらも可 |
@Mapper
public interface Mapper{
public String select();
public void register();
public void update();
public void delete();
}
<select id="select" resultType="String">SQL</select>
<insert id="register">SQL</insert>
<update id="update">SQL</update>
<delete id="delete">SQL</delete>
文字列代入
MyBatisでは2つの方法で文字列の代入が行える。
#{}
はエスケープ処理がされる。安全で高速。公式の推奨記法。
${}
はエスケープ処理がされない。文字列をそのまま代入したい場合などに利用。
#{hoge}
${piyo}
値オブジェクトへのマッピング
値オブジェクトへDBの値をマッピングする際は、マッピング先を明示的に記載する必要がある。
SELECT hoge as "piyo.value";
モデル例
モデルはある程度簡略化してます。
下記のモデルはドメイン貧血症になっていますが、MyBatisを扱う上で必要な情報は揃っているため割愛。
package com.example.demo.domain.model;
public class Person {
Name name;
Age age;
public Person(Name name, Age age) {
this.name = name;
this.age = age;
}
@Deprecated
public Person(){}
public Name name(){
return this.name;
}
public Age age(){
return this.age;
}
}
package com.example.demo.domain.model;
public class Name {
String value;
public Name(String value) {
this.value = value;
}
@Deprecated
public Name(){}
public String value(){
return this.value;
}
}
package com.example.demo.domain.model;
public class Age {
String value;
public Age(String value) {
this.value = value;
}
@Deprecated
public Age(){}
public String value(){
return this.value;
}
}
<select id="hoge" resultType="com.example.demo.domain.model.Person">
SELECT
name as "name.value",
age as "age.value"
FROM hogehoge
</select>
値オブジェクトから値を取り出す
文字列代入などでクラス内のデータを取り出したい時は、下記の3パターンの方法がある。
2つ以上引数を持つ場合は、@Param("")
で引数を一意に識別できるようにする必要がある。
モデル例
モデルはある程度簡略化してます。
下記のモデルはドメイン貧血症になっていますが、MyBatisを扱う上で必要な情報は揃っているため割愛。
package com.example.demo.domain.model;
public class Person {
Name name;
Age age;
public Person(Name name, Age age) {
this.name = name;
this.age = age;
}
@Deprecated
public Person(){}
public Name name(){
return this.name;
}
public Age age(){
return this.age;
}
}
package com.example.demo.domain.model;
public class Name {
String value;
public Name(String value) {
this.value = value;
}
@Deprecated
public Name(){}
public String value(){
return this.value;
}
}
package com.example.demo.domain.model;
public class Age {
String value;
public Age(String value) {
this.value = value;
}
@Deprecated
public Age(){}
public String value(){
return this.value;
}
}
@Mapper
public interface Mapper{
public void methodA(Name name);
public void methodB(Person person);
public void methodC(@Param("aaa") Name name, @Param("bbb") Age age);
}
#{value} // Name.javaのインスタンス変数 value から値を取得
#{name.value} //Personクラスのインスタンス変数 name 内の value から値を取得
#{age.value} //Personクラスのインスタンス変数 age 内の value から値を取得
#{aaa.value}
#{bbb.value}
resultMap
複雑なオブジェクトへデータをマッピングする際に利用。
オプション | 説明 |
---|---|
id | 識別子。一意に特定できる名前を付ける |
type | 戻り値のオブジェクトの型 |
property | 値のマッピング先 |
column | マッピングしたいデータが入っているDBのカラム名 |
resultMapを使う時はSELECT hoge as piyo ~
のpiyo
の部分を、マッピング先のインスタンス変数ではなくマッピング元のDBのカラム名を書く。
モデル例
モデルはある程度簡略化してます。
下記のモデルはドメイン貧血症になっていますが、MyBatisを扱う上で必要な情報は揃っているため割愛。
package com.example.demo.domain.model;
public class Manager {
ManagerNumber number;
ManagerName name;
Telephones telephones;
public Manager(ManagerNumber number, ManagerName name, Telephones telephones) {
this.number = number;
this.name = name;
this.telephones = telephones;
}
@Deprecated
public Manager() {
}
public ManagerNumber number(){
return this.number;
}
public ManagerName name(){
return this.name;
}
public Telephones telephones(){
return this.telephones;
}
}
package com.example.demo.domain.model;
public class ManagerNumber {
int value;
public ManagerNumber(int value) {
this.value = value;
}
@Deprecated
public ManagerNumber() {
}
public int value(){
return this.value;
}
}
package com.example.demo.domain.model;
public class ManagerName {
String value;
public ManagerName(String value) {
this.value = value;
}
@Deprecated
public ManagerName() {
}
public String value(){
return this.value;
}
}
package com.example.demo.domain.model.telephone;
public class Telephones {
List<Telephone> list;
public Telephones(List<Telephone> list) {
this.list = list;
}
@Deprecated
public Telephones() {
}
public List<Telephone> asList(){
return Collections.unmodifiableList(this.list);
}
}
package com.example.demo.domain.model.telephone;
public class Telephone {
TelephoneId id;
TelephoneNumber number;
public Telephone(TelephoneId id, TelephoneNumber number) {
this.id = id;
this.number = number;
}
@Deprecated
public Telephone() {
}
public TelephoneId id(){
return this.id;
}
public TelephoneNumber number(){
return this.number;
}
}
<?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.demo.infrastructure.Mapper">
<resultMap id="readAllMap" type="com.example.demo.domain.model.Manager">
<result property="number.value" column="manager_no"/>
<result property="name.value" column="manager_name"/>
<collection property="telephones.list" ofType="com.example.demo.domain.model.telephone.Telephone">
<id property="id.value" column="id"/>
<result property="number.value" column="telephone_number"/>
</collection>
</resultMap>
<select id="readAll" resultMap="readAllMap">
SELECT
ml.manager_no as "manager_no",
ml.manager_name as "manager_name",
tl.id as "id",
tl.telephone_number as "telephone_number"
FROM
manager_list as ml
LEFT OUTER JOIN
telephone_list as tl
ON ml.manager_no = tl.manager_no
</select>
</mapper>
java.lang.IllegalArgumentException: argument type mismatch
Mapperまでは問題なく動くがデータのマッピングが上手くいかない場合、modelに空のコンストラクタが無いせいでエラーになっている可能性あり。
空のコンストラクタを作成する際は@Depricated
を付けて利用非推奨を明示する。