Sql

[MyBatis] MyBatis와 @Builder 사용 시 resultMap 매핑 문제 해결하기

댕주 2024. 10. 27. 23:35

1. 문제 상황

MyBatis를 사용할 때 일반적으로 xml의 <resultMap> 을 통해 SQL 조회 결과를 DTO에 매핑한다.

기본적인 @Getter, @Setter 가 있는 DTO 에서는 resultMap 의 순서와 DTO 필드 순서가 맞지 않더라도 필드 이름을 기준으로 매핑되기 때문에 큰 문제가 발생하지 않는다.

 

하지만 @Builder 를 사용한 DTO 에서는 resultMap 의 필드 순서와 DTO 의 필드 순서가 맞지 않으면 매핑 오류가 발생할 수 있다.

 

2. 코드 예시

<select id="getDB" resultMap="BuilderMap">
    SELECT *
      FROM TABLE_A
</select>

<resultMap id="BuilderMap" type="BuilderDto">
    <result property="age" column="AGE" />
    <result property="name" column="NAME" />
</resultMap>
@Getter
@Builder
public class BuilderDto {
    private String name;
    private int age;
}

 

3. 원인

Java 에서는 일반적으로 모든 클래스가 디폴트로 기본 생성자 (빈 생성자)를 가지고 있지만, 롬복의 @Builder 를 사용하면 이 기본 생성자가 사라진다.

@Builder 는 모든 필드를 초기화할 수 있는 모든 인수 생성자를 생성하는데, 이로 인해 기본 생성자가 자동으로 생성되지 않게 된다.

 

MyBatis 는 결과 매핑 시 다음과 같은 방식을 사용한다

1. 빈 생성자를 호출해 객체를 생성한다

2. 필드에 SQL 결과를 바인딩하는 과정으로 값을 채워 넣는다.

 

기본 생성자가 없는 상태에서 MyBatis가 빈 인스턴스를 생성하려고 하면, MyBatis는 생성자 호출에 실패하게 되고, 그 결과 매핑 오류나 타입 불일치 오류가 발생하는 것이다.

 

4. 해결 방법

@NoArgsConstructor 추가: @Builder 와 함께 @NoArgsConstructor 를 추가하여 MyBatis가 기본 생성자를 사용해 객체를 생성할 수 있게 한다.

 

5. 기본 생성자가 없어도 resultMap 의 필드 순서와 DTO 의 필드 순서가 정확히 일치한다면 오류가 발생하지 않는다.

MyBatis가 @Builder 를 사용한 DTO 에서도 필드 순서와 이름이 정확히 맞을 경우 내부적으로 생성자나 매핑 로직에서 필드 순서로 값을 맞추어 바인딩 할 수 있기 때문이다. 하지만 @NoArgsConstructor 어노테이션을 추가해서 매핑 오류를 예방하는 것이 안전하다.