[spring] PlatformTxManager 롤백 테스트

식빵·2021년 12월 29일
0

Spring Lab

목록 보기
4/33
post-thumbnail

실험하는 차원에서 그냥 한번 짜봤다.

참고: Spring 공식 문서


JUnit4 Test Code

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();
    }
}
profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글