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
객체에 새롭게 바인딩된 것이다.