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 |