[MyBatis] Mapper method has an unsupported return type 오류

dondonee·2024년 3월 28일
0

문제

발생한 오류

org.apache.ibatis.binding.BindingException: Mapper method 'com.knou.board.repository.mybatis.PostMapper.save' has an unsupported return type: class com.knou.board.domain.Post

스프링 부트에서 마이바티스 연동을 확인하려고 간단하게 게시물 객체 Post와 게시물을 저장하는 save() 매퍼 메서드를 만들고 테스트를 해 보았더니 위와 같은 바인딩 오류가 발생했다.


문제 코드

다음은 매퍼 인터페이스와 그것을 구현한 리포지토리 클래스이다. save()는 데이터베이스에 @Insert를 실행한 뒤 Post를 반환한다.

@Mapper
public interface PostMapper {

    @Insert("INSERT INTO post (post_title, post_content) VALUES (#{postTitle}, #{postContent})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    Post save(Post post);
}
@Repository
@RequiredArgsConstructor
public class MyBatisPostRepository implements PostRepository {

    private final PostMapper postMapper;

    @Override
    public Post save(Post post) {
        postMapper.save(post);
        return post;
    }
}


해결 ✅

해결 방법

@Mapper
public interface PostMapper {

    @Insert("INSERT INTO post (post_title, post_content) VALUES (#{postTitle}, #{postContent})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void save(Post post);
}

매퍼 인터페이스의 save() 메서드의 반환 타입을 Post -> void로 변경해야 한다. (int도 가능)


설명

매퍼 인터페이스에서 INSERT, UPDATE, DELETE 쿼리를 수행하는 메서드의 리턴 타입은 void 또는 int이어야 한다. int로 반환하는 경우 쿼리로 인해 영향받은 로우 수를 반환한다.

단, 일반적인 인터페이스의 경우와 달리 매퍼 인터페이스를 구현한 클래스가 메서드를 오버라이딩하는 경우 리턴 타입을 변경할 수 있다.


기존에 작성한 코드를 예로 들면, 매퍼 인터페이스 메서드를 void save()로 수정하고, 이 매퍼 인터페이스를 구현한 MyBatisPostRepository에서는 그대로 Post save()로 메서드를 오버라이딩했는데, 이것은 정상적으로 작동한다.

테스트 코드는 다음과 같다 :

@Transactional
@SpringBootTest
public class MyBatisPostRepositoryTest {

    @Autowired
    PostRepository postRepository;

    @Test
    void save() {
        //given
        Post post = new Post("title 1", "Lorem Ipsum");

        //when
        Post savePost = postRepository.save(post);
        System.out.println("savePost = " + savePost);

        //then
        Post findPost = postRepository.findById(savePost.getId());
        System.out.println("findPost = " + findPost);
        assertThat(findPost).isEqualTo(savePost);
    }
}
savePost = Post(id=16, postTitle=title 1, postContent=Lorem Ipsum)
findPost = Post(id=16, postTitle=title 1, postContent=Lorem Ipsum)

이 테스트는 성공한다. save()Post를 데이터베이스에 저장한 뒤 savePost를 반환했기 때문에 findById()가 이것의 id 값을 참조해 데이터베이스에서 findPost를 조회해올 수 있었다.


한 가지 주의할 점은 save()가 반환한 Post는 저장이 완료된 뒤 데이터베이스에서 새로 조회한 값이 아니라, 메서드에 전달되었던 객체를 그대로 반환한다는 것이다.

id 필드 값은 매퍼에서 @Options(useGeneratedKeys = true, keyProperty = "id")를 사용했으므로 auto-increment된 id 값이 파라미터로 전달된 Post 객체에 새롭게 바인딩된 것이다.

0개의 댓글