この記事について
- MyBatisでネストしたリスト(階層構造)をマッピングする際は注意
- MyBatisでネストしたリストをマッピングする場合、結果を格納するクラスには
@NoArgsConstructor
を付与しないといけない
この問題でとても時間を要したので、備忘録を込めてまとめます。
MyBatisとは
- XML、またはアノテーションを使用してSQL文とオブジェクトをマッピングするフレームワークのこと(O/R マッパー)です
- JavaのO/Rマッパーについては、Java O/Rマッパーの種類と選定方法について調べたことのメモを参考にすると良いかと思います。
- 通常のCRUD操作が可能です
- パラメータの状態により動的にSQLを作成することも可能です
前提
- 以下のようなentityクラスに対応する情報をMyBatisを用いてDBから取得したい
- クラスは全て仮の呼び名です。特に意味はありません。
- 旅行の計画のクラスとその旅行にホテルが紐づいているイメージです
entityクラス
import lombok.AllArgsConstructor; import lombok.Getter; @Getter @AllArgsConstructor public class TravelPlan implements Serializable { private static final long serialVersionUID = 1L; private String ulid; private int status; private String name; private int area; private List<TravelPlanHotelCompanyBelonging> farmerList; private BigInteger createdId; private LocalDateTime createdAt; }
@Data public class TravelPlanHotelCompanyBelonging implements Serializable { private static final long serialVersionUID = 1L; private String travelPlanId; private BigInteger HotelCompanyId; private int planProposalStatus; private BigInteger createdId; private LocalDateTime createdAt; private BigInteger updatedId; private LocalDateTime updatedAt; }
Repositoryクラス
import org.apache.ibatis.annotations.Mapper; @Mapper public interface TravelPlanRepository { TravelPlan selectByTravelPlanId(String travelPlanId); }
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="TravelPlanRepository"> <resultMap id="TravelPlanMap" type="TravelPlan" autoMapping="true"> <id property="ulid" column="ulid" /> <result property="status" column="status" /> <result property="name" column="name" /> <result property="area" column="area"/> <result property="createdId" column="created_id"/> <result property="createdAt" column="created_at"/> <collection property="hotelCompanyList" resultMap="HotelCompanyMap" /> </resultMap> <resultMap id="HotelCompanyMap" type="TravelPlanHotelCompanyBelonging"> <id property="travelPlanId" column="hotel_travel_plan_id" /> <id property="hotelCompanyId" column="company_id" /> <result property="planProposalStatus" column="farmer_plan_proposal_status" /> </resultMap> <sql id="selectTravelPlan"> SELECT tp.ulid , tp.status , tp.name , tp.created_id , tp.created_at , hotel.travel_plan_id AS hotel_travel_plan_id , hotel.company_id , hotel.plan_proposal_status AS travel_plan_proposal_status FROM travel_plans as tp LEFT JOIN travel_plan_hotel_company_belonging as hotel ON hotel.travel_plan_id = tp.ulid </sql> <select id="selectByTravelPlanId" resultMap="TravelPlanMap"> <include refid="selectTravelPlan"/> WHERE tp.ulid = #{TravelPlanId} </select> </mapper>
問題
- こんなエラー文が出た
Caused by: org.apache.ibatis.executor.result.ResultMapException:
Error attempting to get column 'hotel_travel_plan_id' from result set.
Cause: java.lang.NumberFormatException: For input string: "01HBYRVACPMDR2NMVYF69C8W6E"
- データセットから
hotel_travel_plan_id
カラムを取得し、クラスにセットしようとしているところでエラーが出ていました - セットする値である
hotel_travel_plan_id
の型が違うのかと思い、確認するが、きちんとString
で定義しており、エラーメッセージにもFor input string
と表示されていました
解決方法
- 色々漁ってみました
結果
- 結果を格納する親クラスに
@NoArgsConstructor
を付与し、デフォルトコンストラクタを定義する!!!
import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @Getter @AllArgsConstructor @NoArgsConstructor public class TravelPlan implements Serializable { private static final long serialVersionUID = 1L; private String ulid; private int status; private String name; private int area; private List<TravelPlanHotelCompanyBelonging> farmerList; private BigInteger createdId; private LocalDateTime createdAt; }
- 値をセットするための箱を用意してあげる必要があり、そこにMyBatis側でマッピングしていると想定しています
- 少し実験してみました
値をセットする@Setter
やセッターを使えるようになる@Data
など、値をセットするようなアノテーションを使うことでマッピングできるのかが気になったので、実験してみました。以下がその結果です。
# | @Data | @NoArgsConstructor | @Getter | @Setter | マッピングできるか |
---|---|---|---|---|---|
1 | ○ | ○ | - | - | ○ |
2 | ○ | - | - | - | × |
3 | - | ○ | ○ | - | ○ |
4 | - | ○ | ○ | ○ | ○ |
5 | - | - | ○ | ○ | × |
上記の結果より、@NoArgsConstructor
がないとマッピングはできなさそうです。
最後に
- Spring Bootのアノテーションは未だに使いこなせていない気がしています。使いこなすと便利なので、まだまだ勉強を続けていこうと思います。