본문 바로가기
Java & Kotlin/Spring

[Spring] HikariCP, MyBatis를 이용한 DB설정

by heekng 2021. 5. 9.
반응형

[Spring] HikariCP, MyBatis를 이용한 DB설정

Spring에서 DataBase사용을 위한 DBCP에는 Hikari가 있다.

Hikari가 보편적으로 DBCP로 이용되는 이유는 성능이 좋아서이다.

Spring에서 Hikari 사용을 위한 설정과 MyBatis를 이용하는 방법을 알아본다.


HikariCP설정, 사용방법

Hikari의 버전관리, 설치와 기본 설정방법은 이전 다른 설정들과 동일한 방식으로 진행된다.

pom.xml의 <dependency>태그 작성을 통한 설치 -> root-context.xml의 <bean>태그 작성을 통한 DB연동 설정

Hikari를 사용하기 위한 기본 설정

<!-- https://mvnrepository.com.artifact/com.zaxxer/HikariCP -->
<dependency>
	<groupId>com.zaxxer</groupId>
	<artifactId>HikariCP</artifactId>
	<version>2.7.4</version>
</dependency>

pom.xml의 <dependencies>태그 안에 위 태그를 작성 > 저장 후 프로젝트 update

<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
	<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
	<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:XE"/>
	<property name="username" value="hr"/>
	<property name="password" value="hr"/>
</bean>

Oracle사용을 예로 root-context.xml에 위와 같이 작성하여 DataBase접속을 위한 기본 설정을 진행

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
	<constructor-arg ref="hikariConfig"/>
</bean>

바로 아래에 위 태그를 작성하여 hikariConfig설정에 맞는 dataSource를 받아온다.

Hikari 테스트

기존의 DB사용방법

package com.heekng.persistence;

import static org.junit.Assert.fail;

import java.sql.Connection;
import java.sql.DriverManager;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class) // 테스트 코드가 스프링을 실행
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml") // 지정된 클래스나 문자열을 이용해서 필요한 객체들을 스프링 내에 객체로 등록
@Log4j
public class JDBCTests {
	static {
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	@Test
	public void testConnection() {
		// try(Statement) : 소괄호 안에 close를 필요로하는 인스턴스를 작성하면 자동으로 close()를 실행해 준다.
		try(Connection conn =
				DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE",
						"hr",
						"hr")){
			log.info(conn);
		}catch(Exception e) {
			fail(e.getMessage());
		}
	}
}

 

src/test/java폴더에 테스트 패키지, 클래스 생성, buildpath에 ojdbc add

위와 같이 코드를 작성하면 기존 jdbc를 이용하는 방식의 코드이다.

try-catch문의 try바로 뒤 소괄호 안에 statement를 작성하면 코드 실행 후 자동으로 close()를 실행해준다.

위 이미지를 보면 JUnit실행에 잘 연결됨을 확인할 수 있다.

 

Hikari DataSource 사용 시

package com.heekng.persistence;

import java.sql.Connection;

import javax.sql.DataSource;

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 lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class DataSourceTests {
	@Setter(onMethod_ = @Autowired)
	private DataSource dataSource;
	
	@Test
	public void testConnection() {
		try(Connection conn = dataSource.getConnection()){
			log.info(conn);
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

DataSource 객체를 생성하고 @Setter 어노테이션으로 설정을 주입한다.

테스트 시 정상 연결을 확인할 수 있다.


MyBatis 설정, 연결

pom.xml에 MyBatis <dependency> 태그 추가하여 버전관리, 설치

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

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>1.3.2</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>

위와같이 작성하여 myBatis설치 저장 > 프로젝트 업데이트

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource"/>
</bean>

root-context.xml에 위와 같이 <bean>태그 작성하여 mybatis설정

MyBatis연결 확인

package com.heekng.persistence;

import java.sql.Connection;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
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 lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class DataSourceTests2 {
	@Setter(onMethod_ = @Autowired)
	private SqlSessionFactory sqlSessionFactory;
	
	@Test
	public void testConnection() {
		try(SqlSession sqlSession = sqlSessionFactory.openSession(true);
				Connection conn = sqlSession.getConnection()) {
			log.info(sqlSession);
			log.info(conn);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

테스트 패키지에 위와 같이 클래스 작성

SqlSessionFactory 객체 주입 후 sqlSessionFactory 객체를 통해 connection를 받아오고 객체 확인

테스트시 오류 없이 정상적으로 객체가 생성됨을 확인할 수 있다.

MyBatis를 이용해 쿼리문 작성

1. 어노테이션을 이용한 쿼리 전송

package com.heekng.mapper;

import org.apache.ibatis.annotations.Select;

public interface TimeMapper {
	@Select("SELECT SYSDATE FROM DUAL")
	public String getTime();
}

src/main/java폴더에 mapper패키지 생성 후 인터페이스 생성

@Select 어노테이션을 이용해 해당 메소드가 어떤 쿼리문에 대한 응답을 가져올지 쿼리문 작성

root-context에 Namespaces탭에 mybatis체크

<mybatis-spring:scan base-package="com.heekng.mapper"/>

root-context의 Source에 위 태그 작성(경로는 인터페이스의 패키지 경로)

package com.heekng.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.heekng.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.getTime());
	}
}

테스트 서버에서 쿼리문을 작성한 인터페이스의 메소드 실행

정상적으로 쿼리문이 실행됨을 확인할 수 있다.

 

2. xml을 이용한 쿼리 전송

package com.heekng.mapper;

import org.apache.ibatis.annotations.Select;

public interface TimeMapper {
	@Select("SELECT SYSDATE FROM DUAL")
	public String getTime();
	
	public String getTime2();
}

 

메소드를 가지는 인터페이스 작성

<?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.heekng.mapper.TimeMapper">
	<select id="getTime2" resultType="string">
		SELECT SYSDATE FROM DUAL
	</select>
</mapper>

src/main/resources의 META-INF/com/heekng/mapper과 같이 인터페이스와 동일한 경로를 생성을하고, xml파일을 생성한다.

해당 xml파일에 mybatis 이용을 위한 mapper DOCTYPE 태그를 작성한다.

mapper태그는 기존 JSP에서 이용하던것과 형식이 크게 다르지 않지만 namespace가 연결될 인터페이스의 경로를 작성한다.

쿼리문의 id는 인터페이스의 메소드명과 동일하게 작성한다.

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource"/>
	<property name="mapperLocations" value="classpath:/META-INF/com/heekng/mapper/**/*.xml"/>
</bean>

<mybatis-spring:scan base-package="com.heekng.mapper"/>

root-context.xml의 sqlSessionFactory에 xml을 읽는 property태그를 추가한다.

해당 property태그의 value는 쿼리문을 가지고있는 xml파일이 존재하는 경로를 작성하며 **는 모든 경로, *.xml은 그 안의 모든 xml파일을 뜻한다.

package com.heekng.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.heekng.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 TimeMapperTests2 {
	@Setter(onMethod_ = @Autowired)
	private TimeMapper timeMapper;
	
	@Test
	public void getTimeTest() {
		log.info(timeMapper.getTime2());
	}
}

 

테스트 서버에서 쿼리문을 작성한 메소드 실행

정상적으로 날짜와 시간이 출력됨을 확인할 수 있다.

MyBatis는 내부적으로 JDBC의 PreparedStatement를 이용해서 SQL을 처리한다.
따라서 SQL에 전달되는 파라미터는 JDBC에서와 같이 '?'로 치환되어 처리된다.
복잡한 SQL의 경우 '?'로 나오는 값이 제대로 되었는지 확인하기 쉽지 않고,
실행된 SQL의 내용을 정확히 확인하기 어렵기 때문에 log4jdbc-log4j2라이브러리를 사용하여 어떤 값인지를 정확히 확인한다.

콘솔에 출력되는 로그의 수준 제한하기, DB조회되는 내용 테이블처럼 표시하기

위에서 출력된 모습을 보면 알다시피 DBeaver과 같은 DB Tool처럽 보기좋게 콘솔에 표시되지 않는다.

이러한 불편함을 해결하고, 중요하지 않은 log출력을 줄이기 위해 log4j를 수정한다.

pom.xml수정

<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.15</version>
	<exclusions>
		<exclusion>
			<groupId>javax.mail</groupId>
			<artifactId>mail</artifactId>
		</exclusion>
		<exclusion>
			<groupId>javax.jms</groupId>
			<artifactId>jms</artifactId>
		</exclusion>
		<exclusion>
			<groupId>com.sun.jdmk</groupId>
			<artifactId>jmxtools</artifactId>
		</exclusion>
		<exclusion>
			<groupId>com.sun.jmx</groupId>
			<artifactId>jmxri</artifactId>
		</exclusion>
	</exclusions>
	<scope>runtime</scope>
</dependency>

pom.xml에서 위 태그를 찾아 삭제한다.

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

dependencies태그 최 하단에 위 태그를 추가 > 저장 > 프로젝트 업데이트

log4j.xml 수정

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

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

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

src/main/resources의 log4j.xml > logger태그들 최 하단에 위 태그 추가하여 경고 수준 설정

테스트환경에서의 로그 출력 경고수준을 설정하려면 src/test/resources의 log4j.xml도 설정한다.

위와 같이 log4j.xml에 Cannot find DTD 오류가 뜬다면

<!DOCTYPE log4j:configuration SYSTEM "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">

최상단의 !DOCTYPE을 위 태그로 수정하여 해결한다.

log4jdbc.log4j2.properties 생성

src/main/resources에 Untitled Text File 생성하여 이름은 log4jdbc.log4j2.properties로 생성

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

내용에 위처럼 작성하고 저장

root-context.xml의 hikariConfig 수정

<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
	<!-- 기존
	<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
	<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:XE"/>
	-->
	<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"/>
	<property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@localhost:1521:XE"/>
	<property name="username" value="hr"/>
	<property name="password" value="hr"/>
</bean>

기존의 hikariConfig의 driverClassName과 jdbcUrl을 위와같이 수정한다.

이후 테스트시 테이블 형태로 콘솔이 출력됨을 확인할 수 있다.

반응형