やること
- springframeworkとmybatisを使ったデータ取得
- resultMapを使用したオブジェクトへのマッピング
やらないこと
- springframeworkについての話
- postgresについての話
- assosiationやcollection(これはまた今度)
前提
- Repositoryのメソッドを呼び出す処理については言及しない
- 基本的には取得したデータを
System.out.prinln
で出力したものを 実行結果
として記載する
実行環境
- Java8
- springframework 5.3.23
- mybatis 3.5.10
- mybatis-spring 2.0.7
- postgres14.5 (docker)
- postgresql (sql driver) 42.5.0
構成
DB
CREATE TABLE USERS (
USER_ID VARCHAR(32) NOT NULL,
FIRST_NAME VARCHAR(32) NOT NULL,
FAMILY_NAME VARCHAR(32) NOT NULL,
PRIMARY KEY (USER_ID)
);
INSERT INTO USERS (USER_ID, FIRST_NAME, FAMILY_NAME) VALUES ('hogefuga', 'ほげ', 'ふが');
INSERT INTO USERS (USER_ID, FIRST_NAME, FAMILY_NAME) VALUES ('foobar', 'ふー', 'ばー');
User
public class User {
private final String userId;
private final String firstName;
private final String familyName;
public User(final String userId, final String firstName, final String familyName) {
this.userId = userId;
this.firstName = firstName;
this.familyName = familyName;
}
public String toString() {
return String.format(
"User {userId: %s, firstName: %s, familyName: %s}", userId, firstName, familyName);
}
}
UserRepository
interface
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository {
User get(String userId);
}
impl
import com.github.nkiri.core.domain.model.user.User;
import com.github.nkiri.core.domain.model.user.UserRepository;
import org.springframework.stereotype.Repository;
@Repository
public class DefaultUserRepository implements UserRepository {
private final UserMapper userMapper;
public DefaultUserRepository(final UserMapper userMapper) {
this.userMapper = userMapper;
}
public User get(String userId) {
return userMapper.get(userId);
}
}
UserMapper
applicationContext.xml
検証
文字列パラメータを渡してデータ取得
UserMapper
- interfaceで定義した引数をxml側で参照する
import com.github.nkiri.core.domain.model.user.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
User get(String userId);
}
- resultMapの定義はtype属性にマッピングするクラスを指定するだけ
select
要素のresultMap
属性で定義した resultMap
を指定する
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.github.nkiri.core.infrastructure.datastore.UserMapper">
<select id="get" resultMap="userResultMap">
SELECT
USER_ID as userId
, FIRST_NAME as firstName
, FAMILY_NAME as familyName
FROM USERS
<where>
USER_ID = #{userId}
</where>
;
</select>
<resultMap id="userResultMap" type="com.github.nkiri.core.domain.model.user.User"/>
</mapper>
実行結果
User {userId: hogefuga, firstName: ほげ, familyName: ふが}
User.userIdを値オブジェクトとして定義する
変更点
UserId
public class UserId {
private final String id;
public UserId(final String id) {
this.id = id;
}
@Override
public String toString() {
return id;
}
}
User
index e05003d..93c1db8 100644
--- a/core/src/main/java/com/github/nkiri/core/domain/model/user/User.java
+++ b/core/src/main/java/com/github/nkiri/core/domain/model/user/User.java
@@ -2,11 +2,11 @@ package com.github.nkiri.core.domain.model.user;
public class User {
- private final String userId;
+ private final UserId userId;
private final String firstName;
private final String familyName;
- public User(final String userId, final String firstName, final String familyName) {
+ public User(final UserId userId, final String firstName, final String familyName) {
this.userId = userId;
this.firstName = firstName;
this.familyName = familyName;
実行結果(エラー)
- DBから取得したデータをUserのコンストラクタに渡した際に型が不一致でエラーになってる模様
Exception in thread "main" org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Error instantiating class com.github.nkiri.core.domain.model.user.User with invalid types (UserId,String,String) or values (hogefuga,ほげ,ふが). Cause: java.lang.IllegalArgumentException: argument type mismatch
修正
- DBの
USER_ID
カラムから取得した値を UserId
クラスのコンストラクタに渡し、その結果を用いてUser
クラスのコンストラクタを呼び出す
constructor
要素の子要素には、Javaクラスに用意したコンストラクタの引数の順番に合わせて idArg
とarg
要素を並べる必要がある
- 今回は
(UserId, String, String)
の引数を持つコンストラクタが使用される
- 引数を順不同としたい場合は
name
属性を指定する
index 7cc80d5..31b127f 100644
--- a/core/src/main/resources/com/github/nkiri/core/infrastructure/datastore/UserMapper.xml
+++ b/core/src/main/resources/com/github/nkiri/core/infrastructure/datastore/UserMapper.xml
@@ -14,5 +14,17 @@
;
</select>
- <resultMap id="userResultMap" type="com.github.nkiri.core.domain.model.user.User"/>
+ <resultMap id="userIdResultMap" type="com.github.nkiri.core.domain.model.user.UserId">
+ <constructor>
+ <idArg column="userId" javaType="string"/>
+ </constructor>
+ </resultMap>
+
+ <resultMap id="userResultMap" type="com.github.nkiri.core.domain.model.user.User">
+ <constructor>
+ <idArg resultMap="userIdResultMap" column="userId" javaType="com.github.nkiri.core.domain.model.user.UserId"/>
+ <arg column="firstName" javaType="string"/>
+ <arg column="familyName" javaType="string"/>
+ </constructor>
+ </resultMap>
</mapper>
\ No newline at end of file
実行結果
User {userId: hogefuga, firstName: ほげ, familyName: ふが}
値オブジェクトをgetの引数として渡したい
変更点
UserRepository
index ca26f43..28f9f39 100644
--- a/core/src/main/java/com/github/nkiri/core/domain/model/user/UserRepository.java
+++ b/core/src/main/java/com/github/nkiri/core/domain/model/user/UserRepository.java
@@ -5,5 +5,5 @@ import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository {
- User get(String userId);
+ User get(UserId userId);
}
index e288413..ae792da 100644
--- a/core/src/main/java/com/github/nkiri/core/infrastructure/datastore/DefaultUserRepository.java
+++ b/core/src/main/java/com/github/nkiri/core/infrastructure/datastore/DefaultUserRepository.java
@@ -1,6 +1,7 @@
package com.github.nkiri.core.infrastructure.datastore;
import com.github.nkiri.core.domain.model.user.User;
+import com.github.nkiri.core.domain.model.user.UserId;
import com.github.nkiri.core.domain.model.user.UserRepository;
import org.springframework.stereotype.Repository;
@@ -13,7 +14,7 @@ public class DefaultUserRepository implements UserRepository {
this.userMapper = userMapper;
}
- public User get(String userId) {
+ public User get(UserId userId) {
return userMapper.get(userId);
}
}
UserMapper
index 1566960..d604e3a 100644
--- a/core/src/main/java/com/github/nkiri/core/infrastructure/datastore/UserMapper.java
+++ b/core/src/main/java/com/github/nkiri/core/infrastructure/datastore/UserMapper.java
@@ -1,9 +1,10 @@
package com.github.nkiri.core.infrastructure.datastore;
import com.github.nkiri.core.domain.model.user.User;
+import com.github.nkiri.core.domain.model.user.UserId;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
- User get(String userId);
+ User get(UserId userId);
}
実行結果(エラー)
- パラメータで渡している
UserId
クラスに、userId
フィールドのgetterメソッドがないと言われている
- UserMapper.xml内で
#{userId}
としていると自動でUserId.userId
を参照しようとしてくれる模様
- しかし今回は
UserId.id
を参照してほしいのでこれだとダメ
Exception in thread "main" org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'userId' in 'class com.github.nkiri.core.domain.model.user.UserId'
修正
方法1. 参照するフィールド名の変更
id
フィールドを参照するようにしてあげれば良いだけ
index 31b127f..0964be1 100644
--- a/core/src/main/resources/com/github/nkiri/core/infrastructure/datastore/UserMapper.xml
+++ b/core/src/main/resources/com/github/nkiri/core/infrastructure/datastore/UserMapper.xml
@@ -9,7 +9,7 @@
, FAMILY_NAME as familyName
FROM USERS
<where>
- USER_ID = #{userId}
+ USER_ID = #{id}
</where>
;
</select>
方法2. @Paramを使用する(引数が複数の場合に使用する)
- 引数が複数ある場合は
@Param
を使用してxml側で引数の値を参照できるようにする
- 今回は引数1つなので普通はこの方法は取らないらしいが、一応これでも動く
index d604e3a..1154437 100644
--- a/core/src/main/java/com/github/nkiri/core/infrastructure/datastore/UserMapper.java
+++ b/core/src/main/java/com/github/nkiri/core/infrastructure/datastore/UserMapper.java
@@ -3,8 +3,9 @@ package com.github.nkiri.core.infrastructure.datastore;
import com.github.nkiri.core.domain.model.user.User;
import com.github.nkiri.core.domain.model.user.UserId;
import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
@Mapper
public interface UserMapper {
- User get(UserId userId);
+ User get(@Param("userId") UserId userId);
}
index 31b127f..6fe3a2d 100644
--- a/core/src/main/resources/com/github/nkiri/core/infrastructure/datastore/UserMapper.xml
+++ b/core/src/main/resources/com/github/nkiri/core/infrastructure/datastore/UserMapper.xml
@@ -9,7 +9,7 @@
, FAMILY_NAME as familyName
FROM USERS
<where>
- USER_ID = #{userId}
+ USER_ID = #{userId.id}
</where>
;
</select>
実行結果
User {userId: hogefuga, firstName: ほげ, familyName: ふが}
まとめ
resultMap
を使うことによって、DB設計がイケてなくてもJavaクラスの設計に影響を与えずにいい感じにマッピングすることができそう
- associationやcollectionの使い方についてはまた今度調べてみる