mybatisで値オブジェクト(Value Object)を扱う場合のポイント
前置き
現場で mybatis を使い始めたのですが、値オブジェクト(Value Object)とマッピングさせる際に少しハマったので整理しました。
環境
- Spirng Boot
- mybatis
- h2 DataBase
SELECT の結果をオブジェクト内の Value Object にマッピングさせる
以下のようなUserName
という Value Object クラスがあったとします。
package com.example.demo.domain.model; public class UserName { private final String value; public UserName(String value) { this.value = value; } public String getValue() { return this.value; } }
User
クラスがUserName
を保持します。
他にUserName
とRegisterDate
というクラスも保持しています。
package com.example.demo.domain.model; import lombok.Data; @Data public class User { private UserId userId; private UserName userName; private RegisterDate registerDate; }
schema.sql
にDB定義を書きます。
CREATE TABLE users ( id int NOT NULL , user_name VARCHAR(50) , register_date DATE );
テスト用のデータ挿入用のdata.sql
です。
INSERT INTO users VALUES (1, 'Nocchi', '2020-02-01'); INSERT INTO users VALUES (2, 'Kashiyuka', '2020-02-02'); INSERT INTO users VALUES (3, 'A-Chan', '2020-02-03');
UserRepository
にIDからUser
を取得するためのメソッドfindById
を定義します。
package com.example.demo.domain.repository; import com.example.demo.domain.model.User; import com.example.demo.domain.model.UserId; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Mapper @Repository public interface UserRepository { User findById(@Param("userId") UserId userId); }
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.domain.repository.UserRepository"> <select id="findById" resultMap="UserMap" parameterType="map"> select id, user_name, register_date from users where id = #{userId.value} </select> <resultMap id="UserMap" type="com.example.demo.domain.model.User"> <association property="userId" javaType="com.example.demo.domain.model.UserId"> <constructor> <arg name="value" column="id"/> </constructor> </association> <association property="userName" javaType="com.example.demo.domain.model.UserName"> <constructor> <arg name="value" column="user_name"/> </constructor> </association> <association property="registerDate" javaType="com.example.demo.domain.model.RegisterDate"> <constructor> <arg name="value" column="register_date"/> </constructor> </association> </resultMap> </mapper>
ポイント
- Value Object を
select
の Parameter に使用したい場合@Param
アノテーションをつける @Param
を使用する場合、select
のオプションにparameterType="map"
を付与する
上記を行わないと、where id = #{userId.value}
のように Value Object の値をselect
内で使用できませんでした。
- SELECT結果のオブジェクト内の Value Object インスタンスを生成するには、
association
を使用する - Value Object の コンストラクタに値をマップさせるために
constructor
を使用する arg
のオプションname
で、引数名を指定する
以下の部分です。
<association property="userId" javaType="com.example.demo.domain.model.UserId"> <constructor> <arg name="value" column="id"/> </constructor> </association>
結果
テストコードを書いてブレークさせた結果は以下のように。
Value Object のインスタンスも生成されて、値もマップされています。
コード
GitHub に公開しました。