Post

MyBatis의 @Mapper

MyBatis의 @MapperDataMapper (e.g., mapstruct의 @Mapper) 와는 다르다. MyBatis @Mapper는 sql <> 객체 mapping을 처리하는 역할이다. 이 글에서 @Mapper는 MyBatis @Mapper를 지칭한다.

예제 : Member

MyBatis @Mapper를 사용하는 경우에도 @Repository는 사용

  • MyBatis에서 제공하는 @Mapper 는 sql mapper 의미를 지닌다.
  • 따라서 layer는 아래와 같이 표현되어야 한다.
1
2
@Service ---> @Repository ---> @Mapper ----> mapper.xml || annotation-string
              DAO              sql mapping
  • 관습적으로 @Repository와 @Mapper를 동일한 layer로 간주하는 경우가 많은데, 서로 다른 layer로 간주해야 한다.
  • “dao와 mapper의 차이” 로 검색해보면, 마치 두 개념이 같은 추상화 수준이며 서로 양립 불가한 것 처럼 보이는 글도 많다.
  • 양립 불가한 것 처럼 보이는 설명을 도식화 하면 아래와 같다. (Repository와 Mapper 둘 중 하나를 골라서 써라. 라는 뉘앙스로 보인다)
1
2
3
4
5
6
@Service    --->    @Repository      --->    mapper.xml
sqlSession.select("mapper.xml")
ㄴ 이는 엄밀히 말하면 layer 생략은 아니지만 그렇게 보일 수 있다.

@Service    --->    @Mapper          --->    mapper.xml
ㄴ Repository layer가 생략됐다. 좋지 않다.
  • 그러나 둘은 양립 불가한 개념이 아니라, 둘 다 사용해야 하는 별도의 layer다.
  • Repository layer를 생략하는 것은 좋지 않다. @Service@Mapper를 직접 사용하는 케이스는 Repository layer에서 수행해야 하는, 쿼리로 숨길 수 없는 data structure, schema에 대한 추상화를 Service layer에서 수행해야 한다.

Repository layer & DTO는 아래와 같은 물음에 대한 해답이 된다.

  • Repository
    • member_half_tbl, member_the_other_half_tbl 가져 올 때는 join으로 간단하지만, insert/update/delete 할 때는? (굳이 프로시저 써야 할까?)
    • DTO를 사용한다면, Domain Model <> DTO 변환은? 비즈니스 관심사가 아닌데 Service layer에서 해야 할까?
    • persistence io 객체인 DTO field에 대한 null check 등 validation은? 쿼리에서 하기는 껄끄럽고, Service에서 하자니 뭔가 안맞는 경우는?
  • DTO
    • 반대로 성능 때문에 join해야 하거나, join 하지 말아야 하는 경우는?
    • 배치에서 N+1 select를 피하기 위해 여러 비즈니스 모델에 사용되는 데이터를 한 번에 가져와야 한다면?
    • DTO가 없다면 query가 변경될 때 Domain Model도 변경되어야 한다
    • 특히 DTO 없이 Domain Model인 Member를 그대로 사용해 persistence io 하는 경우 자주 보이는 안티패턴이 있는데, [불완전한 member(반쪽) + 불완전한 member(나머지) = 완전한 member] 를 만드는 케이스다.
    • 참고 )[MyBatis] 객체 안의 객체 매핑하기 (ResultMap과 DTO) 방법 사용해서 DTO를 xml 단의 ResultMap으로 숨기는 것도 가능하지만 역할은 거의 동일하다

Repository와 Mapper 둘 다 사용

참고

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