Post

MyBatis에서 record 클래스 사용 시 setter 관련 오류 및 해결방법

Intro

회고할 틈도 없이 바로 또 팀 프로젝트를 강제당해서(?) 작업 중입니다. 짧은 기간의 프로젝트라 팀원들을 위해, 모두가 사용해본 MyBatis를 사용 중 입니다.

최근에 record 클래스에 맛을 들여서 DTO(Data Transfer Object)는 특별한 이유가 있지 않은 이상 record 클래스를 사용하는 편입니다. 그런데 평소대로 MyBatis를 사용하니 record 클래스에서 setter 를 못찾는다고 오류를 내뱉습니다.

상황

저는 아래와 같이 record 클래스로 간단하게 선언을 한 상황입니다.

1
2
3
package com.juniorok.juniorok.dto;

public record Benefit(long id, String name) { }

평소 XML mapper 에는 아래와 같이 선언해서 사용합니다.

1
2
3
4
5
6
7
8
9
<resultMap id="benefitResultMap" type="com.juniorok.juniorok.dto.Benefit">
    <id property="id" column="id" />
    <result property="name" column="name" />
</resultMap>


<select id="findAllBenefitTags" resultMap="benefitResultMap">
    SELECT * FROM benefits;
</select>

그리고 이대로 사용하면, 오류가 발생합니다.

오류 메시지

아래와 같이 Benefit 이라는 클래스에서 id 라는 이름의 프로퍼티를 위한 setter 가 없다고 합니다.

org.apache.ibatis.reflection.ReflectionException: There is no setter for property named ‘id’ in ‘class com.juniorok.juniorok.dto.Benefit’

Release Note

MyBatis 가 record 클래스를 아직 지원을 안하나 싶어서 Release Note(링크)를 찾아보니, 3.5.10에서 수정이 됐습니다.

IllegalAccessException when auto-mapping Records (JEP-359) #2195

제가 본 Exception 과는 다른 Exception 이지만, 같은 문제로 보입니다.

Pull Request

위에 적힌 Issue 에서 PR 링크를 따라 들어가 테스트 쪽을 살펴봅니다.

그러면 아래와 같이 record 클래스를 사용하고 있는 것을 볼 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Property.java
public record Property(int id, String value, String URL) {
  public String value() {
    // Differentiate between method call and field access
    return value + "!";
  }
}

// RecordTypeMapper.java
@Results(id = "propertyRM")
@Arg(column = "id", javaType = int.class)
@Arg(column = "val", javaType = String.class)
@Arg(column = "url", javaType = String.class)
@Select("select val, id, url from prop where id = #{id}")
Property selectProperty(int id);

수정하기

테스트에 작성된 코드를 따라서 코드를 아래와 같이 수정하였고, 이상 없이 출력됨을 확인하였습니다.

1
2
3
4
5
6
7
8
9
10
11
// Benefit.java
package com.juniorok.juniorok.dto;

public record Benefit(long id, String name) { }

// Repository
@Results(id = "benefitResultMap")
@Arg(column = "id", javaType = long.class)
@Arg(column = "name", javaType = String.class)
@Select("SELECT * FROM benefits")
List<Benefit> findAllBenefitTags();

추가 확인 - XML은 안됨

SQL은 모두 XML 파일에 모아두고 있어서, 통일하기 위해 XML로도 실험을 해봤는데 안됩니다. 처음과 마찬가지로 setter를 찾지 못합니다.

1
2
3
4
5
6
7
8
9
10
11
12
<resultMap id="benefitResultMap" type="com.juniorok.juniorok.dto.Benefit">
    <constructor>
        <idArg column="id" javaType="_long" name="id" />
        <arg column="name" javaType="String" name="name" />
    </constructor>
    <id property="id" column="id" />
    <result property="name" column="name" />
</resultMap>

<select id="findAllBenefitTags" resultMap="benefitResultMap">
    SELECT * FROM benefits;
</select>

XML <constructor> 문서(링크)

Outro

XML 에서도 되는 방법을 찾아보고 싶지만, 과연 이 프로젝트를 끝나고 MyBatis 를 사용할 일이 있을지 잘 모르겠어서 해결 방법만 찾아보고 넘어가기로 했습니다.

This post is licensed under CC BY 4.0 by the author.