안녕하세요. J4J입니다.
이번 포스팅은 MyBatis ↔ Repository를 연결하는 다양한 방법들에 대해 적어보는 시간을 가져보려고 합니다.
이 포스팅에서도 보실 수 있듯이 저는 DB 연결을 위해 MyBatis와 Repository를 연결할 때 interface, interface를 구현한 class, mapper를 모두 생성하곤 했습니다.
하지만 최근 interface와 mapper만으로 MyBatis와 Repository를 연결할 수 있는 방법을 알게 되었는데 사용하던 방법보다 더 효율적이라고 느껴지고 지식을 얻은 김에 알고 있는 방법들을 정리하는 포스팅을 하고자 글을 적게 되었습니다.
알고 있는 방법이 2개밖에 없지만 이 2개보다도 더 효율적인 방법을 알게된다면 추후 포스팅을 해보도록 하겠습니다.
이번에 해볼 테스트는 MySQL에 사람정보를 저장하는 테이블을 만들고 MyBatis 설정을 다르게 하여 데이터 저장 및 조회를 하는 단위 테스트를 진행해보려고 합니다.
공통 코드
※ 스프링과 관련된 코드는 모두 STS-3.9.12.RELEASE 버전을 기준으로 작성되었습니다.
[ 1. MySQL에 DB및 테이블 생성 ]
create database variousRepo;
use variousRepo;
create table person(
name varchar(30),
age int,
weight int,
primary key(name)
);
[ 2. pom.xml에 maven 설정 ]
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.spring</groupId>
<artifactId>variousRepo</artifactId>
<name>variousRepo</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.8</java-version>
<org.springframework-version>5.2.11.RELEASE</org.springframework-version>
<org.aspectj-version>1.9.6</org.aspectj-version>
<org.slf4j-version>1.7.30</org.slf4j-version>
</properties>
<dependencies>
...
<!-- MySQL, MyBatis, @Repository -->
<dependency> <!-- 스프링에서 DB처리를 위한 dependency -->
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency> <!-- MySQL 사용을 위한 dependency -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency> <!-- MyBatis 사용을 위한 dependency -->
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency> <!-- 스프링에서 MyBatis 사용을 위한 dependency -->
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency> <!-- 커넥션풀을 위한 dependency -->
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.8.0</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
...
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
...
</build>
</project>
[ 3. web.xml에서 root-context 자바 설정으로 변경 ]
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.spring.variousRepo.config.RootContext</param-value>
</context-param>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
...
</web-app>
[ 4. dto클래스 생성 (com.spring.variousRepo.dto.Person) ]
※ dto에 명시된 어노테이션에 대해 모르신다면? 2021.03.02 - [IT/Spring] - [Spring] Lombok
package com.spring.variousRepo.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;
private int age;
private int weight;
}
[ 5. src/main/resources에 mybatis 설정 파일 생성 (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">
<configuration>
<typeAliases>
<typeAlias type="com.spring.variousRepo.dto.Person" alias="person"/>
</typeAliases>
<mappers>
<mapper resource="mapper/personmapper.xml" />
</mappers>
</configuration>
[ 6. 단위 테스트 생성 (com.spring.variousRepo.repoTest) ]
※ 단위 테스트에 대해 모르신다면? 2021.02.04 - [IT/Spring] - [Spring] 단위 테스트(JUnit Test)
package com.spring.variousRepo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import com.spring.variousRepo.config.RootContext;
import com.spring.variousRepo.dto.Person;
import com.spring.variousRepo.repository.PersonRepository;
import lombok.extern.slf4j.Slf4j;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {RootContext.class})
@Slf4j
public class repoTest {
@Autowired
PersonRepository personRepository;
@Test
public void oneMethod() {
Person person1 = new Person("철수", 25, 74);
Person person2 = new Person("영희", 23, 49);
int res = personRepository.insert(person1);
log.info("첫 번째 데이터 생성: " + res);
res = personRepository.insert(person2);
log.info("두 번째 데이터 생성: " + res);
log.info("모든 데이터");
log.info(personRepository.selectAllPerson().toString());
}
}
코드들이 중간중간 구현이 안되어 있는게 있는데 구현이 안된 파일들은 각각의 방법으로 구현해보도록 하겠습니다.
방법1: @ComponentScan 사용
※ 스프링과 관련된 코드는 모두 STS-3.9.12.RELEASE 버전을 기준으로 작성되었습니다.
@ComponentScan을 사용하는 방법은 제가 기존에 사용하던 방법입니다.
Repository구현 클래스에서 mapper의 namespace를 사용하여 연결하는 방식으로 interface, class, mapper 3가지를 모두 구현해줘야 합니다. 대부분 명칭도 동일하게 할 텐데 말이죠.
해당 방식으로 공통코드에서 구현하지 않은 파일을 작성하면 다음과 같습니다.
[ 1. RootContext 파일 생성 (com.spring.variousRepo.config.RootContext) ]
package com.spring.variousRepo.config;
import org.apache.commons.dbcp2.BasicDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
@Configuration
@ComponentScan(basePackages = {"com.spring.variousRepo.repository"})
public class RootContext {
@Bean
public BasicDataSource dataSource() { // mysql 연동을 위한 dataSource bean 등록
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/variousRepo?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws Exception { // mybatis 사용을 위한 sessionFactory bean 등록
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource());
sqlSessionFactory.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml")); // mybatis 설정파일 등록
return sqlSessionFactory;
}
@Bean
public SqlSessionTemplate sqlSession(SqlSessionFactoryBean sqlsessionFactory) throws Exception { // mybatis 사용을 위한 sqlSession bean 등록
return new SqlSessionTemplate(sqlsessionFactory.getObject());
}
}
[ 2. Repository interface 생성 (com.spring.variousRepo.repository.PersonRepository) ]
package com.spring.variousRepo.repository;
import java.util.List;
import com.spring.variousRepo.dto.Person;
public interface PersonRepository {
public int insert(Person person);
public List<Person> selectAllPerson();
}
[ 3. Repository interface 구현 클래스 생성 (com.spring.variousRepo.repository.PersonRepositoryImpl) ]
package com.spring.variousRepo.repository;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.spring.variousRepo.dto.Person;
@Repository
public class PersonRepositoryImpl implements PersonRepository {
private String ns = "personmapper.";
@Autowired
SqlSession session;
@Override
public int insert(Person person) {
return session.insert(ns + "insert", person);
}
@Override
public List<Person> selectAllPerson() {
return session.selectList(ns + "selectAllPerson");
}
}
[ 4. src/main/resources에 mapper 생성 (mapper/personmapper.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">
<mapper namespace="personmapper">
<insert id="insert" parameterType="person">
insert
into person(name, age, weight)
values (#{name}, #{age}, #{weight})
</insert>
<select id="selectAllPerson" resultType="person">
select name,
age,
weight
from person
</select>
</mapper>
방법2: @MapperScan 사용
※ 스프링과 관련된 코드는 모두 STS-3.9.12.RELEASE 버전을 기준으로 작성되었습니다.
@MapperScan 방식은 이번에 새롭게 알게 된 방식으로 기존의 @ComponentScan에서 필요했던 interface 구현 클래스를 필요로 하지 않습니다.
Repository interface와 mapper만으로 DB에 접근하여 기존에 하던 동작을 그대로 수행할 수 있기 때문에 하나의 Repository에 대해 파일 하나가 줄어드게 되는 것입니다.
별 차이가 없어보이기는 하겠지만 관련 Repository가 10개만 있어도 파일 10개나 줄어드는 놀라운 효과를 보여줄 수 있습니다.
해당 방식으로 공통코드에서 구현하지 않은 파일을 작성하면 다음과 같습니다.
[ 1. RootContext 파일 생성 (com.spring.variousRepo.config.RootContext) ]
package com.spring.variousRepo.config;
import org.apache.commons.dbcp2.BasicDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
@Configuration
@MapperScan(basePackages = {"com.spring.variousRepo.repository"})
public class RootContext {
@Bean
public BasicDataSource dataSource() { // mysql 연동을 위한 dataSource bean 등록
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/variousRepo?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws Exception { // mybatis 사용을 위한 sessionFactory bean 등록
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource());
sqlSessionFactory.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml")); // mybatis 설정파일 등록
return sqlSessionFactory;
}
@Bean
public SqlSessionTemplate sqlSession(SqlSessionFactoryBean sqlsessionFactory) throws Exception { // mybatis 사용을 위한 sqlSession bean 등록
return new SqlSessionTemplate(sqlsessionFactory.getObject());
}
}
@ComponentScan방식과 다른 점은 @ComponentScan대신에 @MapperScan을 등록해줬다는 것입니다.
[ 2. Repository Interface 생성 (com.spring.variousRepo.repository.PersonRepository) ]
package com.spring.variousRepo.repository;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.spring.variousRepo.dto.Person;
@Mapper
public interface PersonRepository {
public int insert(Person person);
public List<Person> selectAllPerson();
}
@ComponentScan방식과 다른 점은 기존의 interface구현 클래스에 있던 어노테이션이 interface에 @Mapper로 새롭게 추가되었다는 것입니다.
[ 3. src/main/resources에 mapper 생성 (mapper/personmapper.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">
<mapper namespace="com.spring.variousRepo.repository.PersonRepository">
<insert id="insert" parameterType="person">
insert
into person(name, age, weight)
values (#{name}, #{age}, #{weight})
</insert>
<select id="selectAllPerson" resultType="person">
select name,
age,
weight
from person
</select>
</mapper>
@ComponentScan방식과 다른 점은 namespace에 mapper를 지칭하는 이름이 들어가는 대신에 매핑될 interface가 들어갔다는 것입니다.
매핑될 interface와 mapper는 interface의 메서드 이름과 mapper의 id값을 일치시켜 원하는 작업을 수행하도록 해주는 방식입니다.
설명드렸던 모든 방법들은 단위 테스트를 진행할 시 다음과 같은 문구가 출력되며 DB에 저장되는 것을 확인할 수 있습니다.
INFO : com.spring.variousRepo.repoTest - 첫 번째 데이터 생성: 1
INFO : com.spring.variousRepo.repoTest - 두 번째 데이터 생성: 1
INFO : com.spring.variousRepo.repoTest - 모든 데이터
INFO : com.spring.variousRepo.repoTest - [Person(name=영희, age=23, weight=49), Person(name=철수, age=25, weight=74)]
파일 구성
이상으로 MyBatis ↔ Repository를 연결하는 다양한 방법에 대해 간단하게 알아보는 시간이었습니다.
읽어주셔서 감사합니다.
'Spring > Spring' 카테고리의 다른 글
[Spring] JUnit Test에 JNDI 적용 (0) | 2021.04.05 |
---|---|
[Spring] JNDI를 이용한 Tomcat에 Datasource 구성 (3) | 2021.04.04 |
[Spring] 파일 업로드 - MultipartRequest(With. React) (0) | 2021.03.05 |
[Spring] 파일 업로드 - MultipartFile(With. React) (0) | 2021.03.04 |
[Spring] Swagger (0) | 2021.03.03 |
댓글