Helmi

MyBatis + Spring 연동 본문

Spring

MyBatis + Spring 연동

Helmi 2023. 4. 13. 22:15

MyBatis 연동해 좀 더 빠르게 SQL 처리할 수 있는 구조 만듬

 

1. MyBatis

흔히 SQL 매핑 프레임워크로 분류 됨. JDBC 코드의 복잡하고 지루한 작업 피하는 용도로 많이 사용.

전통적인 JDBC 프로그램 MyBatis
- 직접 Connection 맺고 마지막에 close()
- PreparedStatement 직접 생성 및 처리
- PreparedStatement의 setXXX() 등에 대한 모든 작업 개발자가 처리
- SELECT 경우 직접 ResultSet 처리
- 자동으로 Connection close() 가능
- MyBatis 내부적으로 PreparedStatement 처리
- #{prop}와 같이 속성 지정하면 내부적으로 자동 처리
- 리턴 타입을 지정하는 경우 자동으로 객체 생성 및 ResultSet 처리

장점 : 기존의 SQL을 그대로 활용 가능, 진입 장벽 낮은 편이라 JDBC 대안으로 많이 사용

 

mybatis-spring 라이브러리 통해 쉽게 연동작업 처리 가능

예제는 이 구조로

1) MyBatis 관련 라이브러리 추가

pom.xml 파일에 추가적인 라이브러리 설정

 

- spring-jdbc / spring-tx : 스프링에서 데이터베이스 처리와 트랜잭션 처리(해당 라이브러리들은 MyBatis와 무관히 보이지만 추가하지 않은 경우 에러 발생하므로 주의해야 함)

 

- mybatis / mybatis-spring : MyBatis와 스프링 연동용 라이브러리

 

pom.xml

 <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.11</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>3.0.0</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${org.springframework-version></version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${org.springframework-version></version>
</dependency>

 

2) SQLSessionFactory

MyBatis에서 가장 핵심적인 객체는 SQLSession과 SQLSessionFactory

 

SQLSessionFactory : 내부적으노 SQLSession 만들어내는 존재. 개발에서는 SQLSession 통해 Connection 생성하거나 원하는 SQL 전달하고, 결과 리턴받는 구조로 작성하게 됨

 

root-context.xml

<!-- HikariCP configuration -->
	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
		destroy-method="close">
		<constructor-arg ref="hikariConfig" />
	</bean>
	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"?
	<property name="dataSource" ref="dataSource"> </property>
	</bean>

스프링에 SqlSessionFactory 등록 작업은 SqlSessionFactoryBean이용. 스프링과 연동작업 처리하는 mybatis-spring  라이브러리의 클래스


2. 스프링과 연동 처리

SQL 어떻게 처리할지 별도 설정 분리해주고, 자동으로 처리되는 방식을 이용하는 것이 편리

이를 위해 MyBatis의 Mapper 작성

 

Mapper : SQL과 그에 대한 처리 지정하는 역할. MyBatis-spring 이용하는 경우 Mapper를 XML과 인터페이스+어노테이션 형식으로 작성 가능

 

1) Mapper 인터페이스

Mapper를 작성하는 작업은 XML 이용 가능, 예제는 최소한 코드 작성하는 Mapper인터페이스 사용해볼 것임

com.helmi.mapper 패키지 만들고 TimeMapper 인터페이스 추가 (src/main/java)

 

TimeMapper 인터페이스

package com.helmi.mapper;

import org.apache.ibatis.annotations.Select;

public interface TimeMapper {

	@Select("SELECT sysdate FROM dual")
	public String getTime();
}

 

Mapper 설정

MyBatis가 동작시 Mapper 인식할 수 있도록 root-context.xml에 추가 설정 필요. 

가장 간단한 방식은 <mybatis:scan> 태그 이용

 

root-context.xml의 Namespaces에서 mybatis-spring 선택

 

root-context.xml 일부

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource"> </property>
	</bean>
	
	<mybatis-spring:scan base-package="com.helmi.mapper"/>

	<context:component-scan
		base-package="com.helmi.sample">
	</context:component-scan>

<mybatis-spring:scan> 태그의 base-package 속성은 지정 패키지의 모든 MyBatis 관련 어노테이션 찾아 처리.

 Mapper 설정 작업은 각각 XML이나 Mapper 인터페이스 설정 가능하나 매번 너무 번잡하므로 자동으로 패키지 인식하는 방식으로 작성하는 것이 가장 편리함.

 

2) Mapper 테스트

MyBatis-Spring은 Mapper 인터페이스 이용해 실제 SQL 처리가 되는 클래스 자동 생성

따라서 개발자들은 인터페이스와 SQL만 작성하는 방식으로 모든 JDBC 처리 가능

 

src/test/java/com.helmi.persistence.TimeMapperTest.java 클래스

package com.helmi.persistence;

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.SpringJUnit4ClassRunner;

import com.helmi.mapper.TimeMapper;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class TimeMapperTests {

	@Setter(onMethod_=@Autowired)
	private TimeMapper timeMapper;
	
	@Test
	public void testGetTime() {
		log.info(timeMapper.getClass().getName());
		log.info(timeMapper.getTime());
	}
}

Java설정의 경우는 @ContextConfiguration(classes = {com.helmi.config.RootConfig.class})

 

위의 코드가 정상 동작하면 스프링 내부에는 TimeMapper 타입으로 만들어진 스프링객체(빈)이 존재한다는 뜻

 

3) XML 매퍼와 같이 쓰기

SQL이 복잡하거나 길어지는 경우 어노테이션보다 XML을 이용하는 방식 더 선호하게 됨.

XML 작성해 사용할 때 XML파일 위치와 XML 파일에 지정하는 namespaces 속성이 중요.

XML 파일 위치 경우 Mapper 인터페이스 + XML 동시 사용 가능

 

src/main/resources/com/helmi/mapper 폴더 생성 (반드시 한 번에 하나씩 폴더 생성. 제대로 X -  인식 안될 수 있음)

생성된 폴더에 TimeMapper.xml 파일 생성

TimeMapper 인터페이스

package com.helmi.mapper;

import org.apache.ibatis.annotations.Select;

public interface TimeMapper {

	@Select("SELECT sysdate FROM dual")
	public String getTime();

	public String getTime2();
}

Mapper 인터페이스와 XML 같이 이용해보기 위해 기존 TimeMapper 인터페이스에 추가적 메서드 선언

 

TimeMapper.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.helmi.mapper.TimeMapper">

<select id="getTime2" resultType="string">
SELECT sysdate FROM dual
</select>

</mapper>

MyBatis는 apper 인터페이스와 XML 인터페이스 이름과 namespace 속성값 가지고 판단.

위와 같이 com.helmi.mapper.TimeMapper 인터페이스가 존재하고, XML의 <mapper namespace="com.helmi.mapper.TimeMapper"> 같은 이름 존재 시, 병합해서 처리함.

즉, 메서드 선언은 인터페이스에 존재하고 SQL에 대한 처리는 XML 이용하는 방식.

 

<select> 태그의 id 속성 값은 메서드 이름과 동일하게 맞춰야 함. 이 태그는 resultType 속성 가지는데, 이 값은 인터페이스에 선언된 메서드의 리턴 타입과 동일히 작성

 

 

TimeMapperTests 클래스 일부

@Test
		public void testGetTime2() {
			log.info("getTime2");
			log.info(timeMapper.getTime2());
		}

3. log4jdbc-log4j2 설정

MyBatis는 내부적으로 JDBC의 PreparedStatement 이용해 SQL 처리함. 따라서 SQL에 전달되는 파라미터는 JDBC 같이 '?'로 치환되어 처리 됨. 

이를 확인하기 위해 SQL을 변환해 PreparedStatement에 사용된 '?'가 어떤 값으로 처리되었는지 확인하는 기능 추가. 

SQL 로그 제대로 보기 위해 log4jdbc-log4j2 라이브러리 사용

 

pom.xml 라이브러리 설정

<!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4.1 -->
<dependency>
    <groupId>org.bgee.log4jdbc-log4j2</groupId>
    <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
    <version>1.16</version>
</dependency>

라이브러리 추가 이후

① 로그 설정 파일 추가 작업

②  JDBC 연결 정보 수정

 

src/main/resources/log4jdbc.log4j2.properties 파일 추가

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

log4jdbc 이용시 JDBC 드라이버와 URL 정보 수정해야 함.

 

root-context.xml 일부

	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
	<!-- <property name="driverClassName"
			value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
		<property name="jdbcUrl"
			value="jdbc:log4jdbc:oracle:thin:@localhost:1521:xe"></property> -->

		<property name ="driverClassName"
		value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
		<property name="jdbcUrl"
		value="jdbc:log4jdbc:oracle:thin:@localhost:1521:XE"></property>
		
		<property name="username" value="book_ex"></property>
		<property name="password" value="1234"></property>
	</bean>

dataSource() 메서드에서 변경되는 부분은 JDBC 드라이버의 클래스를 'net.sf.log4jdbc.sql.jdbcapi.DriverSpy'로 수정하는 작업과 JDBC 연결 URL 부분에서 중간에 'log4jdbc' 문자열 추가되는 부분.

이 두 설정이 제대로 되어있지 않으면 데이터베이스의 로그 정상 기록 안됨

 

1) 로그 레벨 설정

테스트 코드 실행 시 많은 양 로그 출력되면서 불편하다 느낄 수 있음. 이때 수정해줌

src/test/resources/log4j.xml 수정

 

src/test/resources/log4j.xml 일부

<!--Root Logger-->
<root>
<priority value="info"/>
<appender-ref ref="console"/>
</root>
</log4j:configuration>

만일 log4jdbc에서 출력되는 로그 조절하고 싶다면 추가적인 <logger> 지정해서 처리

<logger name="jdbc.audit">
		<level value="warn" />
	</logger>	
	
	<logger name="jdbc.resultset">
		<level value="warn" />
	</logger>
	
	<logger name="jdbc.connection">
		<level value="warn" />
	</logger>

기본적인 로그는 info 레벨 - warn 같이 좀 더 높은 레벨 로그만 기록하게 수정하면 테스트코드 실행 시 이전 비해 로그 양 줄어듬.

'Spring' 카테고리의 다른 글

스프링 MVC 기본 구조  (0) 2023.04.14
커넥션 풀 설정  (0) 2023.04.12
스프링 특징, 의존성 주입  (0) 2023.04.12
스프링 설명 조금 + 설정 시 Java Configuration 하는 경우  (0) 2023.04.11
구) 스프링 + Mybatis 실전  (0) 2023.04.07