냐냐한 IT/냐냐한 Spring Boot

Spring Boot + H2 + MyBatis로 사용해보기

소소하냐 2019. 9. 22. 18:08

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 

- MyBatis 공식 사이트 - 시작하기 

- MyBatis 샾(#) 달러($) 차이 

 

 


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