Spring Boot + MyBatis 프로젝트 연습 목록
- 01. 신규 Spring Boot 프로젝트 만들기
- 02. Thymeleaf, spring-boot-devtools 추가
- 03. Spring Boot에 H2 추가
- 04. Spring Boot + H2 + Jdbc로 사용해보기
- 05. Spring Boot + H2 + MyBatis로 사용해보기 (현재 포스트)
드디어 MyBatis를 추가하여 사용하는 부분입니다.
시작하기 전 도메인 객체 추가 및 리팩토링
1. 도메인 객체를 하나 이상 두고 MyBatis를 테스트해보고 싶었기에 Diary 도메인 객체를 추가하였다. Diary 관련 소스는 포스팅에는 남기지 않고 아래에 공유드린 github에서 확인이 가능하다.
- src/main/java 하위에 com.sosohanya.leveldiary.diary 패키지를 만들고 Diary.java 생성
- src/main/java 하위에 com.sosohanya.leveldiary.diary 패키지 하위에 DiaryRepository 인터페이스 생성
- src/main/resources/schema.sql 에 diary 테이블 생성 및 데이터 등록 쿼리 추가
2. Jdbc로 단위 테스트했던 부분을 MyBatis로 바꾸어 단위 테스트하고 싶었기에 공통으로 사용할 수 있는 명칭으로 리팩터링 하였다. (해당 파일 우클릭 > Refactor > Rename)
- JdbcAccountRepositoryTest 리팩토링 (파일명/클래스명 AccountRepositoryTest로 변경, 변수명 변경)
//... 생략 ...
public class AccountRepositoryTest { //변경전 : JdbcAccountRepositoryTest
@Autowired
JdbcAccountRepository accountRepository; //변경전 : jdbcAccountRepository
//... 생략 ...
}
MyBatis 의존성 추가
1. pom.xml에 아래 내용 추가
<dependencies>
... 기존 내용 생략 ...
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
-
참고 : 프로젝트 생성 시에 의존성 선택 화면에서 MyBatis Framework 선택하면 자동으로 추가된다
-
참고 : 데이터베이스 설정이 되어있지 않으면 아래와 같은 오류가 발생 (필자의 경우 이전 포스팅에서 H2로 설정)
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
MyBatis 사용방법 1
- mybatis-spring-boot-starter는 기본적으로 @Mapper 애노테이션을 검색한다. 아래 코드처럼 사용하면 된다.
@Mapper
public interface CityMapper {
@Select("SELECT * FROM CITY WHERE state = #{state}")
City findByState(@Param("state") String state);
}
@SpringBootApplication
public class SampleMybatisApplication implements CommandLineRunner {
private final CityMapper cityMapper;
public SampleMybatisApplication(CityMapper cityMapper) {
this.cityMapper = cityMapper;
}
public static void main(String[] args) {
SpringApplication.run(SampleMybatisApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println(this.cityMapper.findByState("CA"));
}
}
- 이러한 방식은 MyBatis를 사용함으로 SQL문 관리를 쉽게 해주는 장점(소스코드와 SQL의 분리, 타 프로그래밍 언어에 사용할 수 있는 이식성, 동적 SQL, SQL 문의 재사용 등)을 없애버린다. 그러한 이유로 필자는 이 방식이 아닌 classpath를 스캔하는 방식을 사용하기로 한다. (인터페이스와 쿼리 XML을 분리하여 사용도 가능한 듯하다. 아직 이 부분은 해보지 않았기 때문에 우선은 넘어가기로 한다)
MyBatis 사용방법 2 : SQL 분리
1. application.properties 에 아래 내용 추가 (MyBatis 설정 파일이 있는 classpath를 지정 )
mybatis.config-location=classpath:mybatis-config.xml
2. MyBatis 설정 파일 생성. 필자의 경우 /src/main/resources 폴더 아래에 mybatis-config.xml 파일 생성
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- mybatis-3-config.dtd에 주목 -->
<configuration>
<!-- 매핑정보 추가 -->
<mappers>
<mapper resource="mybatis/AccountMapper.xml" />
<mapper resource="mybatis/DiaryMapper.xml" />
</mappers>
</configuration>
3. 위에서 설정한 Mapper 파일 생성 및 설정
- /src/main/resources/mybatis/AccountMapper.xml, DiaryMapper.xml 파일 생성 (DiaryMapper.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"> <!-- mybatis-3-mapper.dtd에 주목 -->
<mapper namespace="Account">
<select id="count" resultType="int">
select count(*) from accounts
</select>
<insert id="save" parameterType="Account" useGeneratedKeys="true" keyProperty="id">
insert into accounts (email)
values (#{email})
</insert>
<update id="update" parameterType="Account">
update accounts set email = #{email}
where id = ${id}
</update>
<delete id="deleteById" parameterType="long">
delete accounts where id = #{id}
</delete>
<delete id="deleteAll">
delete from accounts
</delete>
<select id="findAll" resultType="Account">
select * from accounts
</select>
<select id="findById" parameterType="long" resultType="Account">
select * from accounts where id = #{id}
</select>
<select id="findByEmail" parameterType="string" resultType="Account">
select * from accounts where email = #{email}
</select>
</mapper>
- type(parameterType, resultType..)의 타이핑 줄이기 위한 설정 추가
- typeAliases를 지정하지 않으면 위 예제의 resultType="Account" 은 타입을 찾을 수 없다는 오류가 발생한다. resultType="com.sosohanya.leveldiary.account.Account"로 지정해야 오류가 발생하지 않는다. 너무 길다. 타이핑을 줄이려면 별칭을 지정하기로 한다.
- mybatis-config.xml에 <typeAliases>...</typeAliases> 추가하여 타입 별칭을 지정
방법 1. typeAliases > package 사용 : 도메인 객체가 한 군데 모아져 있을 경우 편리 (필자의 경우 내용별로 패키지를 분리(account, diary)하였기에 사용불가. 아래 예제처럼 상위 패키지로 설정하면 가능하긴 하지만 관계없는 클래스들도 탐색이 되므로 이 방법은 사용하지 않기로 했다. )
방법 2. typeAliases > typeAlias 사용 : 별칭을 지정하여 사용
<!-- 방법 1. typeAliases > package 사용 -->
<configuration>
<typeAliases>
<package name="com.sosohanya.leveldiary"/>
</typeAliases>
... 생략 ...
</configuration>
<!-- 방법 2. typeAliases > typeAlias 사용 -->
<configuration>
<typeAliases>
<typeAlias type="com.sosohanya.leveldiary.account.Account" alias="Account"/>
<typeAlias type="com.sosohanya.leveldiary.diary.Diary" alias="Diary"/>
</typeAliases>
... 생략 ...
</configuration>
- namespace
- 이전 버전에서는 선택사항이었으나 현재는 필수
- fully-qualified name은 길기 때문에 분리하여 타이핑을 줄이는 목적. 위 예에서 id="findById"의 경우 쿼리 호출 시 아래와 같이 사용할 수 있다.
- Fully qualified name : sqlSession.selectOne("Account.findById", id);
- Short Name : sqlSession.selectOne("findById", id); - Short name의 경우 다른 매퍼에서 동일한 이름을 사용할 경우 모호성(ambiguous)으로 인한 오류 발생할 수 있다.
(예를 들어 Account.findById, Diary.findById처럼 두 군데 이상에서 같은 이름 사용할 경우 Short name으로 사용했을 때 오류 발생
- 네임스페이스를 사용면 인터페이스 바인딩이 가능. 현재 예제에서는 이 방법을 사용하지 않지만 사용 예는 다음과 같다.
(더 자세한 내용은 MyBatis 공식 웹사이트 - Getting Started [새창]에서 Exploring Mapped SQL Statements절 참고
<?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="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
package org.mybatis.example;
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
4. 매핑된 쿼리로 데이터 처리 구현
: /src/main/java 하위에 com.sosohanya.leveldiary.account 패키지에 AccountRepository를 구현하는 MybatisAccountRepository 클래스 생성
MybatisAccountRepository.java 소스
package com.sosohanya.leveldiary.account;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class MybatisAccountRepository implements AccountRepository {
@Autowired
private SqlSession sqlSession;
@Override
public int count() {
return sqlSession.selectOne("Account.count");
}
@Override
public long save(Account account) {
sqlSession.insert("Account.save", account);
return account.getId(); //Generated Id
}
@Override
public int update(Account account) {
return sqlSession.update("Account.update", account);
}
@Override
public int deleteById(Long id) {
return sqlSession.delete("Account.deleteById", id);
}
@Override
public void deleteAll() {
sqlSession.delete("Account.deleteAll");
}
@Override
public List<Account> findAll() {
return sqlSession.selectList("Account.findAll");
}
@Override
public Account findById(Long id) {
return sqlSession.selectOne("Account.findById", id);
}
@Override
public Account findByEmail(String email) {
return sqlSession.selectOne("Account.findByEmail", email);
}
}
5. 단위 테스트
: JdbcAccountRepository에 대한 테스트가 아닌 MybatisAccountRepository에 대한 테스트로 변경
//... 생략 ...
public class AccountRepositoryTest {
@Autowired
MybatisAccountRepository accountRepository; //변경전 : JdbcAccountRepository
//... 생략 ...
}
6. mybatis-spring-boot-starter는 의존성에 의해 spring-jdbc를 포함한다. 그런 이유로 pom.xml에서 spring-boot-starter-jdbc를 삭제해도 좋다.
<dependencies>
... 기존 내용 생략 ...
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency> -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
7. 구현한 페이지(AccountController.java)에서도 JdbcAccountRepository가 아닌 MybatisAccountRepository를 호출하도록 변경 후 정상 동작 확인
public class AccountController {
@Autowired
MybatisAccountRepository accountRepository; //변경전 : JdbcAccountRepository jdbcAccountRepository;
//... 생략 ...
}
지금까지의 내용은 github에 branch:005-add-mybatis-and-using[새창]으로 확인하실 수 있습니다
참고 사이트 :
- MyBatis 공식 사이트 - mybatis-spring-boot-autoconfigure
Spring Boot + MyBatis 프로젝트 연습 목록
- 01. 신규 Spring Boot 프로젝트 만들기
- 02. Thymeleaf, spring-boot-devtools 추가
- 03. Spring Boot에 H2 추가
- 04. Spring Boot + H2 + Jdbc로 사용해보기
- 05. Spring Boot + H2 + MyBatis로 사용해보기 (현재 포스트)
'냐냐한 IT > 냐냐한 Spring Boot' 카테고리의 다른 글
MyBatis - Join 매핑하기 (0) | 2019.09.27 |
---|---|
MyBatis Logging 추가 (0) | 2019.09.25 |
Spring Boot + H2 + Jdbc로 사용해보기 (0) | 2019.09.20 |
Spring Boot에 H2 추가 (0) | 2019.09.16 |
Thymeleaf, spring-boot-devtools 추가 (0) | 2019.09.14 |