MyBatis의 Mapper가 지원하는 XML의 기본적인 기능들을 살펴볼것이다. 이 글의 내용은 MyBatis의 공식문서를 기반으로 작성 할 것이며 test코드는 자바11, 스프링 부트2.7 버전에서 테스트를 진행하는 것임을 미리 알린다.
이번 설명에서는 select, insert, Result Maps에 대해 다룰 예정이다.
resultMap의 디테일한 association 과 collection의 기능은 프로젝트 진행시 적절한 예시와 함께 자세히 설명하겠다.
동적 SQL은 다음장에서 보도록 하자.
Select
먼저 select를 통해 Mapper인터페이스와 Mapper XML이 어떻게 매핑 되는지 살펴볼것이다.
MyBatis에서는 SQL 쿼리문을 매퍼 인터페이스와 XML 파일에 정읜 매핑을 통해 실행한다. 이 과정에서 중요한 부분은 매퍼 인터페이스와 XML 파일의 namespace 와 id값이 정확하게 매핑되어야 한다는 점이다.
Mapper Interface
- MemberMapper 인터페이스를 확인해 보면 @Mapper 어노테이션을 통해 매퍼 인터페이스로 인식된다. 이 인터페이스는 MyBatis에 의해 SQL 실행과 매핑되는 메서드 들을 정의한다.
- findById(@Param("id") long id) 는 특정 id 값을 입력받아 Member 객체를 반환하는 메서드 이다. 이 메서드는 나중에 XML 파일에 정의된 XML 파일에서 정의된 SQL 쿼리와 연결된다.
매개변수에 @Param("id") 를 추가하여 SQL 쿼리에서 사용할 변수명으로 매핑한다. 파라미터 표기법은 #{} 안에 어노테이션에서 설정한 key 값을 넣어주면 된다.
XML Mapper
- XML 파일의 <mapper> 태그는 namespace 속성을 통행 이 매퍼가 어느 인터페이스와 연결될 것인지 명확하게 지정한다.
namespace 에 설정되어 있는 com.jinlee.mybatis_study.mapper.MemberMapper와 연결되는 것이다. - <select> 태그에서 id 가 findById 인 쿼리가 정의되어 있으며 parameterType은 long,
resultType 은 com.jinmlee.mybatis_study.vo.Member의 객체를 반환한다.
schema.sql에 미리 member 테이블 을 생성하고 데이터를 하나 주입했다.
select Test 코드
@SpringBootTest
public class MapperTest {
@Autowired
private MemberMapper memberMapper;
@Test
@DisplayName("select Test")
public void selectTest(){
Member member;
member = memberMapper.findById(1L);
System.out.println(member.toString());
}
}
직접 테스트 코드를 진행해보니 매퍼 인터페이스가 XML의 쿼리문을 잘 호출하여 Member 객체를 잘 반환해 준것을 확인할 수 있다.
INSERT
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jinmlee.mybatis_study.mapper.MemberMapper">
/**insert는 key생성과 같은 기능을 위해 몇가지 추가 속성과 하위 엘리먼트를 가진다.
먼저 사용하는 데이터베이스가 자동생성키(예를들면 MySQL과 SQL서버)를 지원한다면
useGeneratedKeys=”true” 로 설정하고 대상 프로퍼티에 keyProperty 를 셋팅할 수 있다.
예를들어 Author 테이블이 id 칼럼에 자동생성키를 적용했다고 하면 구문은 아래와 같은 형태일 것이다.
출처 - MyBatis 공식문서
**/
<insert id="save" useGeneratedKeys="true"
keyProperty="id">
INSERT INTO member (username) values (#{member.username})
</insert>
<select id="findAll" resultType="com.jinmlee.mybatis_study.vo.Member">
SELECT * FROM MEMBER
</select>
<select id="findById" parameterType="long"
resultType="com.jinmlee.mybatis_study.vo.Member" >
SELECT * FROM MEMBER WHERE id = #{id}
</select>
</mapper>
List<Member> findAll();
void save(@Param("member") Member member);
<insert> 태그에 id 를 save 로 설정하고 매퍼인터페이스에 save 메서드를 생성해 주었다. 공식문서를 확인해 보니 insert에 조금 신기한 기능이 있었댜. 그것은 useHeneratedKeys="true" 와 keyProperty="id" 이다. 자동생성키를 지원하는 데이터베이스를 사용하면 매핑되는 메서드의 파라미터값에 id를 자동으로 세팅해 주는 것이다.
테스트 코드를 실행해 보면 정말로 member에 id 값이 세팅되어 있는것을 확인할 수 있으며 member에 세팅된 id와 저장된 member의 id가 같다는 것을 확인 할수 있다.
@Test
@DisplayName("insert Test")
public void insertTest(){
Member member = Member.builder()
.username("insertName")
.build();
memberMapper.save(member);
System.out.println(member.toString());
List<Member> memberList = memberMapper.findAll();
assertThat(memberList.get(0).getId()).isEqualTo(member.getId());
}
resultMap
복잡한 쿼리문을 매핑하거나 컬럼명과 변수명을 다르게 가져오고싶다면 resultMap을 사용할 수 있다.
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class BookDto {
private long memberId;
private String author;
}
bookDto를 하나 생성했다. bookDto에서 유저의 이름을 필요로할때 변수명을 작가를 뜻하는 author를 사용하고 싶다면 resultMap을 사용하면 된다.
<resultMap id="bookMap" type="com.jinmlee.mybatis_study.dto.BookDto">
//coulumn 은 데이터베이스 컬럼명
//property는 매핑되는 객체의 변수명
<id column="id" property="memberId"/>
<result column="username" property="author"/>
</resultMap>
<select id="findTest" parameterType="long"
resultMap="bookMap" >
SELECT id, username FROM MEMBER WHERE id = #{id}
</select>
<resultMap> 태그의 id 이름을 설정해주고 <select> 태그 에서 resultMap에 설정한 id값을 넣어주면 매핑이 된다.
<id>,<result>
모두 한개의 컬럼을 한개의 프로퍼티나 간단한 데이터 타입의 필드에 매핑한다.
둘 사이의 차이점은 id 값은 객체 인스턴스를 비교할 때 사용되는 구분자 프로퍼티로 처리되는 점이다. 이점은 일반적으로 성능을 향상시키지만 특히 캐시와 내포된(nested) 결과 매핑(조인 매핑) 의 경우에 더 효과적이다.
테스트 코드를 실행해 보면 bookDto의 변수명이 데이터베이스의 컬럼명과 다 다르지만 잘 매핑된 것을 확인할 수 있다.
@Test
@DisplayName("resultMapTest")
public void resultMapTest(){
Member member = Member.builder()
.username("resultMap Name")
.build();
memberMapper.save(member);
BookDto BookDto = memberMapper.findTest(member.getId());
System.out.println(BookDto);
}
'스프링 > MyBatis' 카테고리의 다른 글
MyBatis 1차 캐시와 2차캐시 (0) | 2024.09.01 |
---|---|
MyBatis의 Spring에서의 동작원리 (0) | 2024.08.30 |
MyBatis 에서는 누가 DB와 매핑될까? VO? DTO? Entity? (0) | 2024.08.28 |
스프링부트 2.7.x 버전에서 MyBatis 시작하기 (0) | 2024.08.28 |