마이바티스

마이바티스

 

MyBatis 는 JdbcTemplate 보다 더 많은 기능을 제공하는 SQL Mapper 이다.

JdbcTemplate 와 비교해서 MyBatis 의 가장 매력적인 점은 SQL 을 XML 에 편리하게 작성할 수 있고 또 동적 쿼리를 매우 편리하게 작성할 수 있다.

jdbcTemplate

String sql = "update item" +
"set item_name:itemName, price=:price, quantity=:quantity" +
"where id=:id";

 


Mybatis

<update id="update">
	update item
	set item_name = #{itemName},
		price = #{price},
		quantity = #{quantity}
	where id = #{id}
</update>


또한 동적 쿼리도 아주 손쉽게 적용시킬 수 있다.

<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>

 

MyBatis 의존성 추가

build.gradle

implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0'


참고로 뒤에 버전 정보가 붙은 이유는 스프링 부트가 버전을 관리해주는 공식 라이브러리가 아니기 때문이다. 스프링 부트가 버전을 관리해주는 경우 버전 정보를 붙이지 않아도 최적의 버전을 자동으로 찾아주게 된다.

스프링 부트 3.0 이면 MyBatis 3.0.3 version 을 사용해야 한다.
스프링 부트 4.0 이상이라면 MyBatis 4.0.1 로 설정해야 한다.


MyBatis 를 사용하려면 의존성 추가 뿐만 아니라 경로 설정도 해주어야 한다.


application.properties

#MyBatis
mybatis.type-aliases-package=hello.itemservcie.domain
mybatis.configuration.map-underscore-to-camel-case=true
loggin.level.hello.itemservice.repository.mybatis=trace

mybatis.type-aliases-package

마이바티스에서 타입 정보를 사용할 때는 패키지 이름을 적어주어야 하는데,
여기에 명시하면 패키지 이름을 생략할 수 있다.
지정한 패키지와 그 하위 패키지가 자동으로 인식이 된다.

쉼표나 세미콜론으로 구분해서 여러 패키지를 한 번에 등록할 수도 있다.

 

설정 전

<select id="findById" resultType="hello.itemservice.domain.User">
	SELECT * FROM users WHERE id = #{id}
</select>


설정 후
`mybatis.type-aliases-package=hello.itemservice.domain` 로 설정을 하면
해당 경로 안에 있는 것들은 그냥 클래스 이름만 불러도 알아들을 수 있다 라는 인식을 해준다.

<select id="findById" resultType="User">
	SELECT * FROM users WHERE id = #{id}
</select>


만약 서로 다른 패키지에 똑같은 이름의 클래스가 있다면 오류가 발생한다.

domain.shop.Item
domain.game.Item


이러한 경우 예외적으로 `@Alias` 라는 Annotation 을 사용하면 된다.

@Alias("ShopItem")
pulbic class Item {...}

mybatis.configuration.map-underscore-to-camel-case

자바 객체는 주로 카멜(camelCase) 표기법을 사용한다.
`itemName` 처럼 중간에 낙타 봉이 올라와 있는 표기법이다.
반면에 관계형 데이터베이스에서는 주로 언더스코어를 사용하는 snake_case` 표기법을 사용한다.
`item_name` 처럼 중간에 언더스코어를 사용하는 표기법이다.
이렇게 관계로 많이 사용하다 보니 해당 기능을 활성화 하면 언더스코어 표기법을 카멜로 자동 변환해 준다. 따라서 DB 에서 `select item_name` 으로 조회를 해도 객체의 `itemName(setItemName())` 속성에 값이 정상 입력이 된다.

해당 설정을 하지 않으면 DB 에서 user_name 이라는 컬럼을 가져와쓴ㄴ데,
자바의 변수명은 userName 이라면 MyBatis 는 철자가 다르기 때문에 데이터를 넣어주지 않는다.

MyBatis 사용법


인퍼페이스 설정

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

다음으로는 SQL 문이 들어가 있는 XML 을 만들어 주어야 한다.
여기서 짚고 넘어가야 할 부분은 경로 설정 부분이다.
MyBatis 가 XML 파일을 어디서 찾아야 하는지를 알려주어야 한다.
크게 두 가지 방법이 있다.

방법 1
XML 을 따로 보관하기

자바 파일은 java 폴더에, XML 은 resources 폴더에 따로 관리하는 방법이다.
이 경우, XML 파일이 어디에 있는지에 대한 설정 파일(application.properties) 로 위치를 설정해 주어야 한다.

설정 추가(application.properteis)

#resources/mappers 폴더 안에 있는 모든 XML 을 읽기
mybatis.mapper-locations=classpath:mappers/*.xml


여기서 classpath 가 무엇인지 궁금할 수 있는데 개발자가 직접 설정하는 변수가 아니라 스프링이 이미 알고 있는 약속된 시작 지점이다. 즉, `src/main/resources` 가 classpath 라고 생각하면 된다.

방법 2
패키지 경로 동일하게 설정


폴더 구조를 토씨 하나 안 틀리고 똑같이 만들면 자동으로 인식하게 됩니다.

// java 폴더
src/main/java/com/example/mapper/ItemMapper.java

// xml 폴더
src/main/resources/com/example/mapper/ItemMapper.xml


여기까지 이해했으면 아래와 같은 XML 코드를 만들면 된다.

 

<!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" useGeneratedKey="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 <= #{maxPrice}
			</if>
		</where>
	</select>
</mapper>

XML 파일을 생성할 때 주의할 점

XML 파일 내부의 내용은 자바 인터페이스와 연결되어야 한다.
XML 파일 코드를 보면 맨 위에 `<mapper namespace="....">` 부분이 있는데,
이 부분은 반드시 자바 인터페이스의 전체 경로와 똑같아야 한다.

 

XML 파일에서 useGeneratedKeys, keyProperty 설명

<insert id="save" useGeneratedKey="true" keyProperty="id">
	insert into item (item_name, price, quantity)
	values (#{itemName}, #{price}, #{quantity})
</insert>

 


useGeneratedKey 는 데이터베이스가 키를 생성해 주는 `IDENTITY` 전략일 때 사용한다.
`keyProperty` 는 생성되는 키의 속성 이름을 지정한다.
INSERT 가 끝나면 `item` 객체의 `id` 속성에 생성된 값이 입력이 된다.

 

XML 파일에서 `@Param` 설명

 

// Repository
public void update(Long id, ItemUpdateDto updateParam) {
	itemMapper.update(id, updateparam);
}

// mapper
void update(@Param("id") long id, @Param("updateParam") ItemUpdateDto updateParam);

// xml
<update id="update">
	update item
	set item_name=#{updateParam.itemName},
		price=#{updateParam.price},
		quantity=#{updateParam.quantity}
	where id = #{id}
</update>


파라미터가 1개만 있으면 `@Param` 으로 지정하지 않아도 되지만,
파라미터가 2개 이상이면 `@Param` 으로 이름을 지정해서 파라미터를 구분해야 한다.









'Java' 카테고리의 다른 글

ConcurrentHashMap 에 대하여  (3) 2025.06.06
유용한 람다식 함수들  (2) 2025.02.04
자바의 반복문 종류  (1) 2025.02.04
Java 의 Call by Value, Call by Reference  (1) 2025.01.06
간단한 에러 출력 방법과 문제점  (0) 2025.01.02