실험하는 차원에서 그냥 한번 짜봤다.
참고: Spring 공식 문서
package me.dailycode.jdbc;
import static org.junit.Assert.assertEquals;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.function.Consumer;
import javax.sql.DataSource;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
public class JdbcTemplateTest {
// 테스트 동안 기본적으로 필요한 것들은 테스트 코드 실행 전에 미리 선언 및 할당한다.
static HikariDataSource hikariDataSource;
static PlatformTransactionManager txManager;
static TransactionTemplate transactionTemplate;
static JdbcTemplate jdbcTemplate;
// 딱 한번만 호출된다.
@BeforeClass
public static void init() {
HikariConfig hikariConfig = new HikariConfig();
//hikariConfig.setAutoCommit(false); // 기본값은 true
hikariConfig.setDriverClassName("org.postgresql.Driver");
hikariConfig.setJdbcUrl("jdbc:postgresql://localhost:7932/dailyCode");
hikariConfig.setUsername("dailyCode");
hikariConfig.setPassword("dailyCode123");
hikariDataSource = new HikariDataSource(hikariConfig);
txManager = new DataSourceTransactionManager(hikariDataSource);
transactionTemplate = new TransactionTemplate(txManager);
jdbcTemplate = new JdbcTemplate(hikariDataSource);
// 만약에 추후에 @Transactional 을 쓰게 되면 해당 Propagation Level 을 따르게 된다.
// 그게 싫을 때는 아래처럼 하나만 더 세팅해준다.
// transactionTemplate.setPropagationBehavior(Propagation.REQUIRES_NEW.value());
}
// Exception도 받아주는 Consumer 함수형 인터페이스 정의
@FunctionalInterface
interface ConsumerWithException<T> extends Consumer<T> {
@Override
default void accept(T elem) {
try {
acceptThrow(elem);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
void acceptThrow(T elem) throws Exception;
}
/*----------------- PlatformTxManager 테스트 [START] -----------------*/
@Test
public void test_transaction_with_PlatformTxManager() {
Integer before
= jdbcTemplate.queryForObject("select count(*) from notice", Integer.class);
txMngTemplate((jdbcTemp) -> {
jdbcTemplate.update("insert into notice values (?, ?, ?, 'none', null)",
"BN0000000052", "jdbcTestSj", "jdbcTestCn");
throw new Exception();
});
Integer after
= jdbcTemplate.queryForObject("select count(*) from notice", Integer.class);
assertEquals(before, after);
}
private void txMngTemplate(ConsumerWithException<JdbcTemplate> callback) {
// 트랜잭션 속성 정의
DefaultTransactionDefinition transactionDef = new DefaultTransactionDefinition();
transactionDef.setTimeout(2); // 초 단위
TransactionStatus status = txManager.getTransaction(transactionDef);
try {
callback.accept(jdbcTemplate);
txManager.commit(status);
} catch (Exception e) {
System.out.println(e.getMessage());
txManager.rollback(status);
}
}
/*----------------- PlatformTxManager 테스트 [END] -----------------*/
// 모든 테스트가 끝났다면 DB 자원해제
@AfterClass
public static void done() {
hikariDataSource.close();
}
}