본문 바로가기
web/springboot

[springboot] mybatis 사용하기(+mybatis unit test)

by fien 2020. 11. 20.

Springboot에서 Mybatis 사용하기

DB 연결

사용하려는 DB에 대한 의존성을 추가하고 db 관련 프로퍼티를 설정하면 쉽게 연동할 수 있다.

  1. dependency 추가
    연결하는 것이 목적이기 때문에 가벼운 h2 데이터베이스를 사용했다.

    <dependency> 
    <groupId>com.h2database</groupId> 
    <artifactId>h2</artifactId> 
    <scope>runtime</scope> 
    </dependency>
  2. DataSource 설정
    어플리케이션 프로퍼티 파일(application.yml or application.properties)에 DataSource를 설정한다.
    springboot는 connection pool로써 hikariCP를 디폴트로 사용하지만 TomcatCP, Commons DBCP2도 지원한다.
    ** datasource는 db connection을 관리하는 객체이고 이를 위해 자체적으로 CP기능을 구현하고 있다.

    #application.yml 
    spring: 
    datasource: 
        url: jdbc:h2:tcp://localhost/~/test # db url 
        driver-class-name: org.h2.Driver # db driver 생략해도 url 형태를 보고 자동으로 찾아줌 
        username: sa 
        password: 
        schema: schema.sql # ddl file 
        hikari: # ConnectionPool 설정 
        maximum-pool-size=4

    ** springboot에서 datasource url를 설정하지 않으면 자동으로 h2 인메모리 DB로 동작
    ** resource 루트(src/main/resources)에 schema.sql, data.sql 파일로 어플리케이션 시작 시 적용될 DDL 스크립트와 DML 스크립트를 작성할 수 있다. ( 파일 이름은 application.yml에서 변경 가능)

    //schema.sql
    drop table if exists Post;
    drop table if exists Member;
    create table Member
    (
        member_id int primary key auto_increment,
        user_id varchar(50) unique,
        name varchar(50) not null,
        password varchar(50) not null
    );
    
    create table Post
    (
        post_id INT primary key auto_increment,
        member_id int not null,
        content varchar(255),
        foreign key (member_id) references Member(member_id),
        created_at datetime not null default current_timestamp
    );
    //data.sql
    insert into member(user_id, name, password) values('aaa','bespoke','1234');

Mybatis 설정

  1. dependency 추가
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.1.3</version>
    </dependency>
  1. mybatis property 설정
    #application.yml
    mybatis: 
        mapper-location: mapper/**/*.xml # Mybatis mapper.xml 위치 설정
        type-aliases-package: org.example.demo.rest.domain #mapper에서 resultType 간략하게
        configuration:
            map-underscore-to-camel-case=true #java vo 클래스의 카멜케이서를 언더스코어로 바꾸어줌
  • mybatis.mapper-location : mapper xml 파일의 위치를 설정.

  • type-aliases-package : mapper 파일에서 mapping Type의 alias를 지정

  • configuration.map-underscore-to-camel-case : java domain(vo,dto) 클래스의 필드의 카멜 케이스 표기 방식을 db table의 underscore방식의 표기 방식과 매핑해준다.

Mapper 구성

3가지 방식으로 mapper를 사용할 수 있다.

  • mapper xml + DAO pattern

  • mapper xml + Mapper interface

  • annotation + Mapper interface

3번째 방법의 경우는 xml로 매퍼를 따로 작성하지 않고 메서드 어노테이션에서 sql를 작성한다.

Model 작성

domain ~ vo ~ model

@Data
@NoArgsConstructor
@RequiredArgsConstructor
public class Member {
    private Long memberId;
    @NonNull private String userId;
    @NonNull private String name;
    @NonNull private String password;

}

*Mapper.xml 작성

(3번 방식에서는 생략)

<?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.example.demo.rest.repository.MemberMapper">

    <insert id="save" parameterType="Member" useGeneratedKeys="true" keyProperty="memberId">
        INSERT INTO MEMBER (
                user_id
             , name
             , password
        ) values (
               #{userId}
             , #{name}
             , #{password})
    </insert>

    <select id="findOneById" resultType="Member" parameterType="Member">
        SELECT *
          FROM member
         WHERE member_id = #{memberId}
    </select>

    <select id="findAll" resultType="Member">
        SELECT *
          FROM member
    </select>
</mapper>

type-aliases-package 옵션으로 인해 resultType을 간략하게 표기할 수 있다.(org.example.demo.rest.domain.Member → member)

mapper 등록

  • xml, java 설정에서 bean으로 등록 - 하나씩 다 등록해야함

  • 매퍼 스캔 : 클래스 패스를 등록해서 자동 등록xml → <mybatis:scan/> , java → @MapperScan

  • @Mapper 어노테이션 : mapper interface에 @Mapper 어노테이션을 붙이면 자동으로 등록 (spring boot에서 지원)

저는 @Mapper 어노테이션 방식을 사용 했습니다.

Mapper 사용

앞서 말한 3가지 방식으로 매퍼를 사용 할 수 있다.

Dao pattern

SqlSession 객체를 통해 sql을 직접 실행한다.

@Repository 
@RequiredArgsConstructor 
public class MemberDao { 
    private final SqlSession sqlSession; // spring-mybatis 모듈의 구현체인 SqlSessionTemplate를 사용 
    public List<Member> selectList(){ 
        return sqlSession.selectList("org.example.demo.rest.repository.MemberMapper.findAll"); 
    } 
}

mapper interface

mapper interface는 단순히 매퍼 xml상의 id와 메서드명을 일치시켜주면 된다.

@Mapper 
public interface MemberMapper { 
    public List<Member> findAll(); 
}

annotation

xml 파일 없이 인터페이스 메서드 어노테이션으로 쿼리를 작성
** java 모델 객체의 필드 명과 db table의 컬럼 명이 일치하지 않아 매핑을 해줘야 한다. configuration.map-underscore-to-camel-case 옵션을 주면 자동으로 변환해서 매핑해준다.

@Mapper
public interface MemberMapper { 

    @Insert("insert into Member(user_id, name, password) values(#{userId},#{name},#{password})")
    @Options(useGeneratedKeys = true, keyProperty = "memberId")
    public int save(Member member);

    @Select("select * from Member where member_id = #{memberId}") 
    // @Results({ 
    // @Result(property = "memberId", column = "member_id"), 
    // @Result(property = "userId", column = "user_id") 
    // })
    public Member findOneById(Long memberId);

    @Select("select * from Member") 
    public List<Member> findAll(); 
}

Controller에서 Mapper 사용

간단한 curd는 mapper interface를 직접 호출해서 사용하고

로직이 필요한경우 service 클래스 만든다.

@Service
@Transactional
@RequiredArgsConstructor
public class MemberService {
    private final MemberMapper memberMapper;
    public Long join(Member member){
        memberMapper.save(member);
    }
}

Springboot에서 mybatis unit test하기

springboot에서 mybatis unit테스트를 할 때는 @MybatisTest 어노테이션을 사용하면된다.

Mapper interface 방식(2,3번)은 @MybatisTest을 사용하면 autowired가 동작하지만 dao 방식은 해당 dao class를 추가로 import 해줘야 한다.

dao test

@RunWith(SpringRunner.class)
@MybatisTest //spring boot - mybatis test
@Import(MemberDao.class) 
public class MemberDaoTest {
    @Autowired
    private MemberDao memberDao;

}

mapper interface test

@RunWith(SpringRunner.class)
@MybatisTest
public class MemberMapperTest {
    @Autowired
    private MemberMapper memberMapper;
    @Test
    public void save_find_Test(){
        Member member = new Member("a","bbb","1234");
        memberMapper.save(member);
        Member findMember = memberMapper.findOneById(member.getId());
        Assert.assertEquals(saveId,findMember.getMemberId());
    }
}

@WebMvcTest 처럼 다른 Test 어노테이션과 같이 쓸 때는 @MybatisTest가 아닌 @AutoConfigureMybatis 사용하기

MyBatis-Spring-Boot-Starter-Test mybatis 공식문서

http://mybatis.org/spring-boot-starter/mybatis-spring-boot-test-autoconfigure/

다음에는 REST와 springboot에서 REST API를 만드는 것을 포스팅 하겠습니다

댓글