#3 Spring Framework HikariCP&Transaction 설정
2023-06-01
1. DataSource HikariCP로 변경
pom.xml -> commons-dhcp2, commons-pool2, commons-collections 제거, HikariCP 추가
<!-- commons-dhcp2, commons-pool2, commons-collections 제거 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency> -->
<!-- HikariCP 추가 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>
applicationContext.xml -> DataSource 수정
<!-- (기존) DataSource ApachCommons -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- (변경) DataSource HikariCP -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="dataSourceProperties">
<props>
<prop key="cachePrepStmts">true</prop>
<prop key="prepStmtCacheSize">250</prop>
<prop key="prepStmtCacheSqlLimit">2048</prop>
</props>
</property>
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="hikariConfig"/>
</bean>
2. Mapper 생성
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
applicationContext.xml -> sqlSessionFactoryBean -> mybatis-config.xml 생성 및 Mapper 등록
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="kr.co.springlegacy.vo.Board" alias="Board" />
</typeAliases>
<mappers>
<mapper resource="mapper/BoardMapper.xml" />
</mappers>
</configuration>
configuration
: MyBatis 설정 파일의 루트 요소입니다. MyBatis의 전반적인 설정을 포함합니다.typeAliases
: Java 객체와 MyBatis 매퍼 XML 파일에서 사용되는 클래스와 별칭(Alias) 간의 매핑을 정의하는 요소입니다.typeAlias
하위 요소를 사용하여 개별 클래스와 해당 별칭을 매핑할 수 있습니다.typeAlias
: Java 클래스와 별칭 간의 매핑을 정의하는 요소입니다.type
속성에는 매핑할 Java 클래스의 전체 경로를 지정하고,alias
속성에는 해당 클래스에 대한 별칭을 지정합니다.mappers
: MyBatis 매퍼 파일의 위치를 설정하는 요소입니다.mapper
하위 요소를 사용하여 개별 매퍼 파일의 경로를 지정할 수 있습니다.mapper
: MyBatis 매퍼 파일의 경로를 지정하는 요소입니다.resource
속성에 매퍼 파일의 위치를 지정합니다. 매퍼 파일은 SQL 문과 결과 매핑 등을 포함하는 MyBatis의 매퍼 인터페이스와 대응되는 XML 파일입니다.
src/main/resources -> mapper/BoardMapper.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="kr.co.springlegacy.mapper.BoardMapper">
<select id="getTime" resultType="String">
SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') FROM dual
</select>
<select id="getList" resultType="Board">
select * from myboard
</select>
</mapper>
src/main/java -> kr.co.springlegacy.mapper.BoardMapper 인터페이스 생성
package kr.co.springlegacy.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Select;
import kr.co.springlegacy.vo.Board;
public interface BoardMapper {
// @Select("select * from myboard")
public List<Board> getBoardList();
public String getTime();
}
※BoardMapper.xml에 쿼리를 안만들고 어노테이션을 사용해 BoardMapper Interface 안에서 바로 사용할수 있습니다.
src/test/java -> kr.co.springlegacy.mapper.BoardMapperTest 클래스 생성
package kr.co.springlegacy.mapper;
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 kr.co.springlegacy.mapper.BoardMapper;
import kr.co.springlegacy.vo.Board;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class BoardMapperTest {
@Autowired
private BoardMapper boardMapper;
@Test
public void testInsert() {
System.out.println(boardMapper.getBoardList());
System.out.println(boardMapper.getTime());
}
}
JUnit Test 실행 결과
3. Service <-> Mapper 연결
src/main/java -> kr.co.springlegacy.service.BoardService 인터페이스 생성
package kr.co.springlegacy.service;
import java.util.List;
import kr.co.springlegacy.vo.Board;
public interface BoardService {
public List<Board> getBoardList();
}
src/main/java -> kr.co.springlegacy.service.BoardServiceImpl 클래스 생성
package kr.co.springlegacy.service;
import java.util.List;
import org.springframework.stereotype.Service;
import kr.co.springlegacy.mapper.BoardMapper;
import kr.co.springlegacy.vo.Board;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class BoardServiceImpl implements BoardService{
private final BoardMapper boardMapper;
public List<Board> getBoardList(){
return boardMapper.getBoardList();
}
}
applicationContext.xml -> Component-scan 설정
<context:component-scan base-package="kr.co.springlegacy.service" />
src/test/java -> kr.co.springlegacy.service.BoardServiceTest 클래스 생성
package kr.co.springlegacy.service;
import java.util.List;
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 kr.co.springlegacy.vo.Board;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class BoardServiceTest {
@Autowired
private BoardService boardService;
@Test
public void getBoardListTest() {
List<Board> boardList = boardService.getBoardList();
boardList.forEach(board -> {
System.out.println(board.toString());
});
}
}
JUnit Test 실행 결과
4. 트랜잭션 관리
pom.xml -> aspectjwweaver, aspectjrt 추가
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
applicationContext.xml -> DataSourceTransactionManager, tx:annotaion-drven 추가
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" />
<tx:annotation-driven transaction-manager="transactionManager"/>
BoarderServiceImpl 클래스/메소드 @Transactional 추가 및 BoardMapper.xml -> insertBoard 쿼리 수정
Transaction 테스트를 위해 insertBoard메소드에 boardMapper.insertBoard(board) 입력을 반복시키고
insertBoard 쿼리는 Seq를 입력받을수 있도록 수정한다.
package kr.co.springlegacy.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import kr.co.springlegacy.mapper.BoardMapper;
import kr.co.springlegacy.vo.Board;
import lombok.RequiredArgsConstructor;
@Transactional
@Service("boardService")
@RequiredArgsConstructor
public class BoardServiceImpl implements BoardService{
private final BoardMapper boardMapper;
@Override
public List<Board> getBoardList(){
System.out.println("getBoardList() 메소드");
return boardMapper.getBoardList();
}
@Override
@Transactional
public void insertBoard(Board board) {
System.out.println("insertBoard() 메소드");
boardMapper.insertBoard(board);
boardMapper.insertBoard(board);
}
}
<!--
<insert id="insertBoard" parameterType="Board">
INSERT INTO myboard (seq, title, writer, content)
VALUES((select nvl(max(seq), 0)+1 from myboard), #{title}, #{writer}, #{content})
</insert>
-->
<insert id="insertBoard" parameterType="Board">
INSERT INTO myboard (seq, title, writer, content)
VALUES(#{seq}, #{title}, #{writer}, #{content})
</insert>
src/test/main -> kr.co.springlegacy.BoardServiceTest 클래스
package kr.co.springlegacy.service;
import java.util.List;
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 kr.co.springlegacy.vo.Board;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class BoardServiceTest {
@Autowired
private BoardService boardService;
@Test
public void insertBoardTest() {
Board board = Board.builder()
.seq(29)
.title("타이틀")
.writer("작성자")
.content("내용")
.build();
boardService.insertBoard(board);
}
}
JUnit Test 실행 결과
첫번째 boardMapper.insertBoard(board);
정상적으로 실행이되고
두번째 boardMapper.insertBoard(board);
에서 기본키인 Seq가 unique 제약조건 오류가 발생되면서
트랜잭션이 걸려 있는 Servcie단위로 롤백이 된다.
댓글남기기