MyBatis란?
MyBatis란 SQL Mapper기술의 한 종류로 JdbcTemplate과 마찬가지로 반복적인 Jdbc 코드를 대신 처리해주고 데이터베이스 테이블과 객체를 매핑하는 편리한 방법을 제공해준다.
MyBatis는 JdbcTemplate과는 달리 xml을 기반으로 sql을 작성할 수 있으며 JdbcTemplate의 단점이었던 동적쿼리 문제를 해결해준다.
이제 본격적인 사용법을 알아보자.
MyBatis 사용하기
사용법을 이해하기 위한 테스트케이스는 다음과 같다.
1. 아이템 도메인(Item)
@Data
public class Item {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String itemName;
private Integer price;
private Integer quantity;
public Item() {
}
public Item(String itemName, Integer price, Integer quantity) {
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
2. 아이템 리포지토리(ItemRepository)
public interface ItemRepository {
Item save(Item item);
void update(Long itemId, ItemUpdateDto updateParam);
Optional<Item> findById(Long id);
List<Item> findAll(ItemSearchCond cond);
}
- Item save(Item item): 아이템 객체를 데이터베이스에 저장
- void update(Long itemId, ItemUpdateDto updateParam): 아이템 객체를 id를 통해 업데이트
- Optional<Item> findById(Long id): id를 통해 아이템 객체 찾기
- List<Item> findAll(ItemSearchCond cond): 특정 조건의 Item 모두찾기
3. 아이템 검색 조건(ItemSearchCond)
@Data
public class ItemSearchCond {
private String itemName;
private Integer maxPrice;
public ItemSearchCond() {
}
public ItemSearchCond(String itemName, Integer maxPrice) {
this.itemName = itemName;
this.maxPrice = maxPrice;
}
}
설정하기
1) build.gradle
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
2) application.properties
#MyBatis
mybatis.type-aliases-package=hello.itemservice.domain
mybatis.configuration.map-underscore-to-camel-case=true
logging.level.hello.itemservice.repository.mybatis=trace
1) mybatis.type-aliases-package:
- 마이바티스에서 타입정보를 사용할 때는 패키지 이름가지 적어줘야 하는데 해당속성을 통해 명시하면 패키지 이름을 생략할 수 있음
- 지정한 패키지와 하위패키지가 자동으로 인식됨
- 여러 위치를 지정하려면 , 혹은 ;를 사용하면 된다.
2) mybatis.configuration.map-underscore-to-camel-case=true:
- JdbcTemplate의 BeanPropertyRowMapper 에서 처럼 언더바를 카멜로 자동 변경해주는 기능을 활성화
3) logging.level.hello.itemservice.repository.mybatis=trace:
- Mybatis에서 실행되는 쿼리로그를 확인할 수 있다.
@Mapper
MyBatis에서 가장 핵심이 되는 Mapper 인터페이스를 지정해주는 어노테이션이다.
MyBatis는 Mapper 어노테이션이 붙은 인터페이스와 xml을 통해 동적으로 구현 클래스를 만들어준다.
ItemMapper.java
@Mapper
public interface ItemMapper {
void save(Item item);
void update(@Param("id") Long id, @Param("updateParam")ItemUpdateDto updateParam);
Optional<Item> findById(Long id);
List<Item> findAll(ItemSearchCond itemSearch);
}
각 메서드의 이름은 xml에서의 id값이 된다.
XML
Mapper 인터페이스의 기능을 xml 파일을 통해 작성한다.
Mybatis는 동적으로 Mapper 구현 클래스를 만들 때 xml파일의 내용을 통해 만든다.
xml파일을 작성할 때 몇가지를 주의해야 하는데 이는 다음과 같다.
1. Mapper와 파일명이 같아야 한다.
예를 들어 Mapper 인터페이스의 파일명이 ItemMapper.java라면 ItemMapper.xml이어야 한다.
2. 파일 경로
1) src/main/resources 아래 있어야 한다.
2) Mapper 인터페이스와 같은 패키지아래 있어야 한다.
예를 들어서 ItemMapper.java의 경로가 src/main/java/hello/itemservice/repository/mybatis/ItemMapper.java라면
ItemMapper.xml파일의 경로는 src/main/resources/hello/itemservice/repository/mybatis/ItemMapper.xml이어야한다.
물론 별도의 설정을 통해 자유롭게 지정할 수 있다.
application.properties를 수정하면 된다.
mybatis.mapper-locations=classpath:mapper/**/*.xml
이렇게 수정시, src/main/resources/mapper를 포함한 그 하위폴더에 있는 xml파일을 매핑파일로 인식한다.
이 경우 파일 이름까지 자유롭게 설정할 수 있다.
ItemMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="hello.itemservice.repository.mybatis.ItemMapper">
<insert id="save" useGeneratedKeys="true" keyProperty="id">
insert into item (item_name, price, quantity)
values (#{itemName}, #{price}, #{quantity})
</insert>
<update id="update">
update item
set item_name=#{updateParam.itemName},
price=#{updateParam.price},
quantity=#{updateParam.quantity}
where id = #{id}
</update>
<select id="findById" resultType="Item">
select id, item_name, price, quantity
from item
where id = #{id}
</select>
<select id="findAll">
select id, item_name, price, quantity
from item
<where>
<if test="itemName != null and itemName != ''">
and item_name like concat('%', #{itemName}, '%')
</if>
<if test="maxPrice != null">
and price <= #{maxPrice}
</if>
</where>
</select>
</mapper>
1. save
void save(Item item);
<insert id="save" useGeneratedKeys="true" keyProperty="id">
insert into item (item_name, price, quantity)
values (#{itemName}, #{price}, #{quantity})
</insert>
- <insert>
- INSERT 쿼리를 실행한다.
- id
- Mapper 인터페이스에서의 메서드 명이다.
- useGeneratedKeys="true"
- 기본키를 데이터베이스에서 생성해주는 IDENTITY 전략을 사용할 때 true로 설정한다.
- keyProperty
- 생성되는 기본키에 해당하는 속성
- 파라미터
- #{}의 형식으로 파라미터를 바인딩한다.
- 객체의 속성을 적어주면 된다.
2. update
import org.apache.ibatis.annotations.Param;
void update(@Param("id") Long id, @Param("updateParam") ItemUpdateDto updateParam);
<update id="update">
update item
set item_name=#{updateParam.itemName},
price=#{updateParam.price},
quantity=#{updateParam.quantity}
where id = #{id}
</update>
- <update>
- UPDATE 쿼리를 실행한다.
- @Param
- 파라미터가 두 개 이상인 경우 @Param을 통해 이름을 지정해줘야 한다.
- xml에선 지정한 이름을 사용하면 된다.
3. findById
Optional<Item> findById(Long id);
<select id="findById" resultType="Item">
select id, item_name, price, quantity
from item
where id = #{id}
</select>
- <select>
- SELECT 쿼리를 실행한다.
- resultType
- SELECT 쿼리를 통해 반환할 타입을 지정한다.
- 원래는 패키지명까지 함께 명시해줘야 한다. 하지만 application.properties에서 mybatis.type-aliases-package를 통해 지정해주면 패키지명을 생략할 수 있다.
- JdbcTemplate의 BeanPropertyRowMapper처럼 자동으로 객체를 매핑시켜준다.
- 네이밍 관례 불일치의 경우(카멜 케이스 - 스네이크 케이스)도 application.properties에서 mybatis.configuration.map-underscore-to-camel-case를 true로 설정하면 해결할 수 있다.
4. findAll
List<Item> findAll(ItemSearchCond itemSearch);
<select id="findAll" resultType="Item">
select id, item_name, price, quantity
from item
<where>
<if test="itemName != null and itemName != ''">
and item_name like concat('%',#{itemName},'%')
</if>
<if test="maxPrice != null">
and price <= #{maxPrice}
</if>
</where>
</select>
- <where>
- MyBatis가 동적 쿼리를 위해 제공하는 태그
- 만약 <where>안에 있는 if가 모두 실패하면 아무것도 추가하지 않는다.
- 만약 <where>안에 있는 if가 하나라도 성공하면 첫번째 나오는 and를 where로 변환해준다.
- <if test="">
- test에 해당하는 조건이 만족하면 태그안의 내용을 추가한다.
- 부등호 사용
- 부등호를 나타내는 <, >의 경우 태그를 시작하고 끝내는 문자이기 때문에 조심히 사용해야한다.
- MyBatis에서는 부등호를 다음과 같이 나타낸다
- <: <
- >: >
- &: &
MyBatis 구조
MyBatis가 동작하는 구조는 다음과 같다.
1. @Mapper 어노테이션이 붙은 인터페이스를 조회한다.
2. @Mapper 인터페이스와 xml 파일을 기반으로 ItemMapper 동적 프록시 객체를 생성한다.
3. 생성된 동적 프록시 객체를 스프링 빈으로 등록한다.
정리
1. MyBatis: JdbcTemplate의 동적쿼리 단점을 보완한 SQL Mapper. @Mapper와 xml 파일을 사용
2. @Mapper: MyBatis의 핵심 인터페이스로 메서드 정의
3. xml: @Mapper 인터페이스의 구현사항을 xml로 정의
해당 포스트는 김영한 님의 스프링 DB 2편 - 데이터 접근 활용 기술 강의를 기반으로 작성되었습니다.
출처: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2
'Spring > Spring Data' 카테고리의 다른 글
[Spring Data] Spring Data JPA (0) | 2024.06.07 |
---|---|
[Spring Data] JPA (0) | 2024.06.07 |
[Spring Data] JdbcTemplate (0) | 2024.06.06 |
[Spring Data] SQL Mapper VS ORM (0) | 2024.06.05 |
[Spring Data] 예외로 인한 의존성과 스프링의 예외 추상화 (0) | 2024.04.30 |