[Spring] 스프링 DB 2편 04

알재·2023년 12월 1일

스프링 DB

목록 보기
10/17

김영한님의 스프링 DB 2편 을 공부하여 정리한 글입니다.

MyBatis 소개

MyBatis는 더 많은 기능을 제공하는 SQL Mapper 이다.
MyBatis의 가장 매력적인 점은 SQL을 XML에 편리하게 작성할 수 있고
또 동적 쿼리를 매우 편리하게 작성할 수 있다는 점이다.

비교

SQL 여러줄

String sql = "update item " +
	"set item_name=:itemName, price=:price, quantity=:quantity " +
	"where id=:id";
<update id="update">
	update item
	set item_name=#{itemName},
	price=#{price},
	quantity=#{quantity}
	where id = #{id}
</update>

동적 쿼리

String sql = "select id, item_name, price, quantity from item";
//동적 쿼리
if (StringUtils.hasText(itemName) || maxPrice != null) {
	sql += " where";
}
boolean andFlag = false;
if (StringUtils.hasText(itemName)) {
	sql += " item_name like concat('%',:itemName,'%')";
	andFlag = true;
}
if (maxPrice != null) {
	if (andFlag) {
	sql += " and";
	}
	sql += " price <= :maxPrice";
}
<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 &lt;= #{maxPrice}
    	</if>
	</where>
</select>

MyBatis 설정

build.gradle

//MyBatis 추가
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0'
//MyBatis 스프링 부트 3.0 추가
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.1'

propertise

#MyBatis
mybatis.type-aliases-package=hello.itemservice.domain
mybatis.configuration.map-underscore-to-camel-case=true
logging.level.hello.itemservice.repository.mybatis=trace
  • mybatis.type-aliases-package
    • 마이바티스에서 타입 정보를 사용할 때는 패키지 이름을 적어주어야 하는데,
      여기에 명시하면 패키지 이름을 생략할 수 있다.
    • 지정한 패키지와 그 하위 패키지가 자동으로 인식된다.
    • 여러 위치를 지정하려면 , , ; 로 구분하면 된다.
  • mybatis.configuration.map-underscore-to-camel-case
    JdbcTemplate의 BeanPropertyRowMapper 에서 처럼 언더바를 카멜로 자동 변경해주는 기능을 활성화 한다.
  • logging.level.hello.itemservice.repository.mybatis=trace
    MyBatis에서 실행되는 쿼리 로그를 확인할 수 있다.

MyBatis 적용1 - 기본

ItemMapper

@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);
}
  • MyBatis 매핑 XML을 호출해주는 매퍼 인터페이스이다.
  • @Mapper 애노테이션을 붙여주어야 한다. 그래야 MyBatis에서 인식할 수 있다.
  • 이 인터페이스의 메서드를 호출하면 다음에 보이는 xml 의 해당 SQL을 실행하고 결과를 돌려준다.

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" 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 #lt;= #{mapPrice}
            </if>
        </where>
    </select>
</mapper>
  • namespace : 앞서 만든 매퍼 인터페이스를 지정하면 된다.
  • insert - save
    • id 에는 매퍼 인터페이스에 설정한 메서드 이름을 지정하면 된다.
      여기서는 메서드 이름이 save() 이므로 save 로 지정하면 된다.
    • 파라미터는 #{} 문법을 사용하면 된다. 그리고 매퍼에서 넘긴 객체의 프로퍼티 이름을 적어주면 된다.
    • useGeneratedKeys 는 데이터베이스가 키를 생성해 주는 IDENTITY 전략일 때 사용한다.
      keyProperty 는 생성되는 키의 속성 이름을 지정한다.
  • update - update
    여기서는 파라미터가 Long id , ItemUpdateDto updateParam 으로 2개이다.
    파라미터가 1개만 있으면 @Param 을 지정하지 않아도 되지만,
    파라미터가 2개 이상이면 @Param 으로 이름을 지정해서 파라미터를 구분해야 한다.
  • select - findById
    resultType 은 반환 타입을 명시하면 된다. 여기서는 결과를 Item 객체에 매핑한다.
  • select - findAll
    if 는 해당 조건이 만족하면 구문을 추가한다.
    where 은 적절하게 where 문장을 만들어준다.

MyBatis 적용2 - 설정과 실행

Repository

@Repository
@RequiredArgsConstructor
public class MyBatisItemRepository implements ItemRepository {

    private final ItemMapper itemMapper;

    @Override
    public Item save(Item item) {
        itemMapper.save(item);
        return item;
    }

    @Override
    public void update(Long itemId, ItemUpdateDto updateParam) {
        itemMapper.update(itemId, updateParam);
    }

    @Override
    public Optional<Item> findById(Long id) {
        return itemMapper.findById(id);
    }

    @Override
    public List<Item> findAll(ItemSearchCond cond) {
        return itemMapper.findAll(cond);
    }
}

Config

@Configuration
@RequiredArgsConstructor
public class MyBatisConfig {

    private final ItemMapper itemMapper;

    @Bean
    public ItemService itemService() {
        return new ItemServiceV1(itemRepository());
    }

    @Bean
    public ItemRepository itemRepository() {
        return new MyBatisItemRepository(itemMapper);
    }
}

MyBatis 적용3 - 분석

ItemMapper 매퍼 인터페이스의 구현체가 없는데 어떻게 동작한 것일까?

업로드중..

  • 애플리케이션 로딩 시점에 MyBatis 스프링 연동 모듈은 @Mapper 가 붙어있는 인터페이스를 조사한다.
  • 해당 인터페이스가 발견되면 동적 프록시 기술을 사용해서 ItemMapper 인터페이스의 구현체를 만든다.
  • 생성된 구현체를 스프링 빈으로 등록한다.

profile
저장소

0개의 댓글