見出し画像

【Javaお勉強日記】mybatisを使って独自クラスとカラムに対して複雑なマッピングをする

必要になったのでまとめます。


0.サービスを作る

// BookService.java
public class BookService{
   BookRepository bookRepository;
   public BookService(BookRepository bookRepository){
       this.bookRepository = bookRepository;
   }
   // 中略...
   public Book getBookDataByCode(String bookCode){
       return bookRepository.selectBookDataByCode(bookCode);
   }       
}

図書管理アプリを作る想定。サービスから、Stringで冊子コードを渡してリポジトリを呼ぶようにしておきます。関連データをひとまとまりにして返したいので、戻り値はBook型にしておきます。

サービス側の挙動としては「getしてくる」、だけど、リポジトリ側の挙動は「SQL使ってselectする」なので、リポジトリ側のメソッドはselectと命名することにしてみます。その辺りはお好みとかプロジェクトの宗教によって。

1.戻り値を入れるためのBookクラスを用意する

サービスからまとめて返したいデータ=DBから取得したいカラムと同じフィールドを持つクラスを作る。

//Book.java
import lombok.Data
@Data
public class Book{
    String id;
    String title;
    String author;
    int price;
}

lombok.Dataをインポートして、クラスに@Dataを付与しておけば、面倒なことは裏で全部やってくれるので、最低限これだけ書いておけばOK。

特に事情が無ければ、DBのカラム名と一致するように作って置くと後々が楽。だけど、DBのカラム名がhoge_hugaみたいにスネークケースとかhoge-hugaみたいなケバブケースで、変数名はhogeHugaとキャメルケースに統一したい、みたいな場合はあとでマッピングできるので統一しなくてもよい。

2.リポジトリインターフェイスにメソッドを宣言する

前回といっしょ。戻り値の型だけBookに変更。

// BookRepository.java
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface BookRepository {
    Book selectBookDataByCode(String bookCode);
}

戻り値をBookにするためにBook型のインポートも必要なので忘れずに。

3.XMLのマッパーを書く

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="com.project.application.repository.book.BookRepository">
	<select id="selectBookDataByCode" resultType="com.project.domain.model.Book" parameterType="string">
       SELECT
           id,
           title,
           author,
           price
       FROM sample.database
       WHERE bookcode = #{bookcode}
	</select>
</mapper>

とりあえずSELECT文を書く。SELECTするカラムはBookクラスのフィールドが持っているものだけにする。

今回は、ResultTypeを指定して、結果がBookクラスに入るようにする。独自クラスをResultTypeに指定する場合、importする時に使うのと同じアドレスをべたっと書く。

DBのカラム名と戻り値型のフィールド名が全て一致している場合は、これだけで裏で勝手にマッピングしてくれる。

4.ResultMapを作る

DBのカラム名とフィールド名が一致しないとか、DBにはコードで入っている情報を、別のテーブル参照して日本語に戻してから追加したいとか、そういう一手間が必要な場合、XMLファイル内にResultMapを定義しておいて、手動でマッピングする。

<!-- <mapper>内部に -->
<resultMap id="bookMap"  type="com.project.domain.model.Book" >
	<result property="title" column="title" />
	<result property="autherName" column="auther_name" />
	<result property="price" column="price" />
	<association property="publisher" column="publisher_code" javaType="string" select="_findPublisherName" />
</resultMap>

<select id="__findPublisherName" parameterType="string" resultType="string">
    SELECT
        publisher_name
    FROM
        publisher
    WHERE
        publisher_id = {#publisher_id}
</select>


例えば、出版社の情報は、DBにはコードだけで入れておいて、表示する時に出版社テーブルから日本語名を持ってこよう、という実装になっている場合。

<association>タグを使うことで、別のselect文の結果をさらに取ってくることができる。javaTypeはstringとかintとかlong辺りのプリミティブ型は用意されてるのでそのまま入れられるし、独自クラスを用意してもいい。

で、本命のSELECT文を書いてあるselectタグのresultType属性の代わりに、resultMap="resultMapId"を使えば、このマッピングを適用して結果を受け取ることができる。

5.検索結果をListで受け取る

検索結果が複数件になる場合、勝手にListにして渡してくれる。便利。

その場合も、ResultTypeは一件当たりのデータを入れる用の型を書けば良い。(Stringだけの1列×複数行を取得するなら、resultType="String"にしておけば、戻り値は自動でList<String>になる)

6.パラメータをリストで渡す

// BookRepository.java
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface BookRepository {
    List<Book> selectBookDatasByMultipulCodes(List<String> bookCodes);
}

リポジトリ側で、引数の型をList<String>型で宣言しておく。

XML側では、

<select id="selectBookDatasByMultipulCodes" resultType="com.project.domain.model.Book" parameterType="list">
    SELECT
       id,
       title,
       author,
       price
    FROM sample.database
    WHERE bookcode in
    <foreach item="bookCodes" collection="list" open="(" separator="," close=")">
        #{bookCodes}
    </foreach>
</select>

てな具合に、parameterTypeをlistにして、foreachタグを使ってリストの中身を全部取り出して使う。

さらに、Mapとか、オブジェクトとかの、キーと値のペアが格納されているものを引数で渡した場合、

#{object.key}

を使えばvalueを使える。





この記事が気に入ったらサポートをしてみませんか?