이제 본격적으로 애플리케이션 코드에 트랜잭션 매니저를 적용해보겠습니다.

MemberRepositoryV3


/**
 * 트랜잭션 - 트랜잭션 매니저
 * DataSourceUtils.getConnection()
 * DataSourceUtils.releaseConnection()
 */
@Slf4j
public class MemberRepositoryV3 {

    private final DataSource dataSource;

    public MemberRepositoryV3(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Member save(Member member) throws SQLException {
        ...
    }

    public Member findById(String memberId) throws SQLException {
        ...
    }

    public void update(String memberId, int money) throws SQLException {
        ...
    }

    public void delete(String memberId) throws SQLException {
        ...
    }

		//...2
    private void close(Connection con, Statement stmt, ResultSet rs) {
        JdbcUtils.closeResultSet(rs);
        JdbcUtils.closeStatement(stmt);
        //주의! 트랜잭션 동기화를 사용하려면 DataSourceUtils를 사용해야 한다.
        DataSourceUtils.releaseConnection(con, dataSource);
    }

		//...1
    private Connection getConnection() throws SQLException {
        //주의! 트랜잭션 동기화를 사용하려면 DataSourceUtils를 사용해야 한다.
        Connection con = DataSourceUtils.getConnection(dataSource);
        log.info("get connection={} class={}", con, con.getClass());
        return con;
    }

}

MemberRepositoryV2 와 다르게 커넥션을 파라미터로 전달하는 부분을 모두 제거했습니다. save(), findById(), update(), delete() 는 기존 코드와 동일합니다.

  1. DataSourceUtils.getConnection()

  2. DataSourceUtils.releaseConnection()

MemberServiceV3_1

이제 트랜잭션 매니저를 사용하는 서비스 코드를 작성해봅시다.

import hello.jdbc.domain.Member;
import hello.jdbc.repository.MemberRepositoryV3;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.sql.SQLException;

/**
 * 트랜잭션 - 트랜잭션 매니저
 */
@RequiredArgsConstructor
@Slf4j
public class MemberServiceV3_1 {

		//...1
    private final PlatformTransactionManager transactionManager;
    private final MemberRepositoryV3 memberRepository;

    public void accountTransfer(String fromId, String toId, int money) {
        //트랜잭션 시작 ...2,3
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            bizLogic(fromId, toId, money);
		        //...4
            transactionManager.commit(status); //성공시 커밋
        } catch (Exception e) {
		        //...5
            transactionManager.rollback(status); //실패시 롤백
            throw new IllegalStateException(e);
        }
    }

		//...6
    private void bizLogic(String fromId, String toId, int money) throws SQLException {
        Member fromMember = memberRepository.findById(fromId);
        Member toMember = memberRepository.findById(toId);
        memberRepository.update(fromId, fromMember.getMoney() - money);
        validation(toMember);
        memberRepository.update(toId, toMember.getMoney() + money);
    }

    private void validation(Member toMember) {
        if (toMember.getMemberId().equals("ex")) {
            throw new IllegalStateException("이체중 예외 발생");
        }
    }

}

  1. PlatformTransactionManager
  2. getTransaction()
  3. new DefaultTransactionDefinition()
  4. transactionManager.commit(status)
  5. transactionManager.rollback(status)
  6. MemberServiceV2 와 다르게 파라미터로 넘기던 커넥션을 제거합니다.