kotlin+spring boot를 사용중인데, MyBatis에서 Collection ResultMap을 구성하며 겪었던 삽질? 에 대해 공유하고자 한다.
결론부터 말하자면, 모든 필드에 기본값을 정의해야 한다. (기본 생성자 : default constructor 가 존재해야 하므로)
커머스 분야인 우리는, 데이터 구조를 크게
주문번호 : 상품번호 = 1:n
상품번호 : 옵션번호 = 1:n 으로 관리하고 있다.
당연하게도, 1개의 주문은 최소 1개의 상품번호와 최소 1개의 옵션번호를 가진다.
그래서 모든 필드는 NotNull이다.
@Alias("elasticSearchStatisticSyncSearchView")
data class ElasticSearchStatisticSyncSearchView(
val orderNo: String,
val registerType: String,
val orderProduct: List<ElasticSearchOrderProduct>,
)
data class ElasticSearchOrderProduct(
val orderNo: String,
val orderProductNo: String,
val registerType: String,
val orderProductOption: List<ElasticSearchOrderProductOption>,
)
data class ElasticSearchOrderProductOption(
val orderNo: String,
val orderProductNo: String,
val orderProductOptionNo: String,
val registerType: String,
)
<resultMap id="elasticSearchStatisticSyncSearchView" type="ElasticSearchStatisticSyncSearchView">
<id property="orderNo" column="so_order_no"/>
<result property="registerType" column="so_register_type"/>
<collection property="orderProduct" resultMap="stOrderProduct" javaType="java.util.List"/>
</resultMap>
<resultMap id="stOrderProduct" type="ElasticSearchOrderProduct">
<id property="orderProductNo" column="sop_order_product_no"/>
<result property="orderNo" column="sop_order_no"/>
<result property="registerType" column="sop_register_type"/>
<collection property="orderProductOption" resultMap="stOrderProductOption"
javaType="java.util.List"/>
</resultMap>
<resultMap id="stOrderProductOption" type="ElasticSearchOrderProductOption">
<id property="orderProductOptionNo" column="sopo_order_product_option_no"/>
<result property="orderNo" column="sopo_order_no"/>
<result property="orderProductNo" column="sopo_order_product_no"/>
<result property="registerType" column="sopo_register_type"/>
</resultMap>
그렇게 db에서 데이터를 긁어오는 API를 실행해봤는데,,,
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Content-Length: 121
{
"path": "/... {비밀}",
"status": 500,
"code": "E0000",
"message": "... {비밀}"
}
500 에러가 발생했다.
를 확인해보니
Caused by: org.apache.ibatis.reflection.ReflectionException: Error instantiating class
com.ncp.order.core.domain.statistic.model.ElasticSearchStatisticSyncSearchView with invalid types (String,String,List) or values ({orderNo},{registerType},{orderProductNo}).
Cause: java.lang.IllegalArgumentException: argument type mismatch
타입이 맞지 않는단다.
게다가 ElasticSearchStatisticSyncSearchView의 타입은 (String, String, List) 인데 실제로는 orderNo, registerType, orderProductNo가 들어온게 이상하다.
해당 오류메시지 (Mybatis invalid types) 를 검색했더니 스택 오버플로우의 게시글을 볼 수 있었다. : https://stackoverflow.com/questions/16774448/mybatis-and-invalid-types
답변 내용은 간단했다. You need to have an empty constructor
kotlin 코드를 java코드로 변환해보면, data class의 생성자가 java 코드로 어떻게 변환되는지 알 수 있다. : kotlin 코드를 java로 변환
data class ElasticSearchStatisticSyncSearchView(
val orderNo: String,
val registerType: String,
val orderProduct: List<ElasticSearchOrderProduct>,
)
위와 같이 아무런 초기값이 없는 경우 java 코드로는
모든 필드를 받는 1개의 생성자만 생성이 된다.
data class ElasticSearchStatisticSyncSearchView(
val orderNo: String = "", // 1개의 기본값
val registerType: String,
val orderProduct: List<ElasticSearchOrderProduct>,
)
기본값이 1개가 있는 경우는
생성자가 오버로딩이 된다.
data class ElasticSearchStatisticSyncSearchView(
val orderNo: String = "",
val registerType: String = "",
val orderProduct: List<ElasticSearchOrderProduct>? = null,
)
모든 필드에 기본값이 있는 경우는
기본 생성자가 오버로딩된다.
아무튼, 아까 stack overflow에서 기본 생성자가 존재해야한다고 했기 때문에 모든 필드에 기본값을 넣어본다.
@Alias("elasticSearchStatisticSyncSearchView")
data class ElasticSearchStatisticSyncSearchView(
val orderNo: String = "",
val registerType: String = "",
val orderProduct: List<ElasticSearchOrderProduct>? = null,
)
data class ElasticSearchOrderProduct(
val orderNo: String = "",
val orderProductNo: String = "",
val registerType: String = "",
val orderProductOption: List<ElasticSearchOrderProductOption>? = null,
)
data class ElasticSearchOrderProductOption(
val orderNo: String = "",
val orderProductNo: String = "",
val orderProductOptionNo: String = "",
val registerType: String = "",
)
기본값만 추가가 됐다. 그리고 다시 돌려보면
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 450811
[
{
"orderNo": "{주문번호}",
"registerType": "{타입}",
"orderProduct": [
{
"orderNo": "{주문번호}",
"orderProductNo": "{상품번호1}",
"registerType": "{타입}",
"orderProductOption": [
{
"orderNo": "{주문번호}",
"orderProductNo": "{상품번호1}",
"orderProductOptionNo": "{옵션번호1}",
"registerType": "{타입}"
},
{
"orderNo": "{주문번호}",
"orderProductNo": "{상품번호1}",
"orderProductOptionNo": "{옵션번호2}",
"registerType": "{타입}"
}
]
},
{
"orderNo": "{주문번호}",
"orderProductNo": "{상품번호2}",
"registerType": "{타입}",
"orderProductOption": [
{
"orderNo": "{주문번호}",
"orderProductNo": "{상품번호1}",
"orderProductOptionNo": "{옵션번호3}",
"registerType": "{타입}"
}
]
}
]
}
... 중략
과 같이 잘 출력된다.
Reference
https://stackoverflow.com/questions/16774448/mybatis-and-invalid-types