Free Lines Arrow
본문 바로가기
Spring/Spring DB 스터디

[Spring DB] 트랜잭션 적용 방식

by skahn1215 2022. 9. 11.
728x90
반응형

트랜잭션 적용 방식

  • 트랜잭션의 적용 방식을 알아보자.
  • 어떻게 트랜잭션 코드가 개선되는지 확인해본다.
  • AOP를  이용하여 트랜잭션 코드와 비즈니스 로직을 분류 해본다.

 

트랜잭션 의 적용 방식 종류

선언적 트랜잭션 관리

  • @Transaction
    - AOP 사용
    - Proxy 구조

프로그래밍 방식의 트랜잭션 관리

  • JDBC 트랜잭션
    - 코드 작성 필요
  • 트랜잭션 템플릿
    - 코드 작성 필요

 

트랜잭션 사용 준비

  • 앞서 커넥션 풀에 대해 공부 했다.
  • 트랜잭션을 사용하기 위해선 동일한 커넥션을 사용해야 된다.

 

JDBC 트랜잭션 적용

  • 동일한 커넥션을 사용하기 위해 커넥션을 파라미터로 넘겨주자.
  • accountTransfer 메소드 시작 할때 dataSource 에서 커넥션을 가져온다.
  • 커넥션의 오토 커밋을 false 로 하여 트랜잭션을 직접 다루도록 한다.
  • bigLogic 을 보면 파라미터로 커넥션을 받는다 이는 동일한 커넥션을 사용하기 위함이다.
public void accountTransfer(String fromId, String toId, int money) throws SQLException {

        Connection con = dataSource.getConnection(); // 커넥션 획득

        try {
            con.setAutoCommit(false); // 트랜잭션 시작
            // 비즈니스 로직 수행

            bizLogic(fromId, toId, money, con);
            con.commit(); // 성공시 커밋
        } catch (Exception e) {
            con.rollback();
            throw new IllegalStateException(e);
        } finally {
            release(con);

        }

    }

    private void bizLogic(String fromId, String toId, int money, Connection con) throws SQLException {
        Member fromMember = memberRepositoryV2.findById(con, fromId);
        Member toMember = memberRepositoryV2.findById(con, toId);

        memberRepositoryV2.update(con, fromId, fromMember.getMoney() - money);
        validation(toMember);
        memberRepositoryV2.update(con, toId, toMember.getMoney() + money);
    }

    private void release(Connection con) {
        if (con != null) {
            try {
                con.close();
            } catch (Exception e) {
                log.info("error", e);
            }
        }
    }

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

 

문제점

  • JDBC 코드 와 비즈니스 로직이 섞여있어 코드의 복잡성이 올라갔다.
  • 트랜잭션에 종속 되어 다른 트랜잭션을 사용하는 경우에도 불필요하게 적용이 된다

 

트랜잭션 매니저 사용

  • 트랜잭션 매니저는 트랜잭션이 시작된 커넥션을 트랜잭션 동기화 매니저에 보관한다.
  • 그렇기 때문에 트랜잭션 매니저에서 트랜잭션을 꺼내오면 쉽게 사용할 수 있다.
private final PlatformTransactionManager transactionManager;
private final MemberRepositoryV3 memberRepository;

public void accountTransfer(String fromId, String toId, int money) throws SQLException {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            // 비즈니스 로직 수행
            bizLogic(fromId, toId, money);
            transactionManager.commit(status); // 성공시 커밋
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw new IllegalStateException(e);
        }
    }

 

문제점

  • 여전히 중복되는 코드들이 있다
  • commit 을 해줘야 하고 익셉션 시 롤백을 해줘야 되는 코드가 존재 한다.

 

 

트랜잭션 템플릿 사용

  • 트랜잭션 템플릿을 사용하게 되면 다음과 같은 이점이 있다.
  • 비즈니스 로직에서 체크드 익셉션이 발생 하게 되면 커밋이 된다.
  • 비즈니스 로직에서 언체크드 익셉션이 발생하게 되면 롤백이 된다.
  • 참고로 트랜잭션 템플릿을 사용하려면 트랜잭션 매니저를 생성자에 넣어줘야 한다.
    private final TransactionTemplate txTemplate;
    private final MemberRepositoryV3 memberRepositoryV3;

    public MemberServiceV3_2(PlatformTransactionManager transactionManager, MemberRepositoryV3 memberRepositoryV3) {
        this.txTemplate = new TransactionTemplate(transactionManager);
        this.memberRepositoryV3 = memberRepositoryV3;
    }

    public void accountTransfer(String fromId, String toId, int money) throws SQLException {

        // 아래 복잡한 로직을 txTemplate 을 사용해서 해결한다.
        // 안에서 안체크 예외가 터지면 롤백을 한다.
        // 체크 예외인 경우 커밋함.
        txTemplate.executeWithoutResult((status)->{
            try {
                bizLogic(fromId, toId, money);
            } catch (SQLException e) {
                throw new IllegalStateException(e);
            }
        });
    }

    private void bizLogic(String fromId, String toId, int money) throws SQLException {
        Member fromMember = memberRepositoryV3.findById(fromId);
        Member toMember = memberRepositoryV3.findById(toId);

        memberRepositoryV3.update(fromId, fromMember.getMoney() - money);
        validation(toMember);
        memberRepositoryV3.update(toId, toMember.getMoney() + money);
    }

 

문제점

  • 여전히 트랜잭션 관련 코드가 존재한다.

 

스프링의 트랜잭션 AOP 등장

  • 비즈니스 로직에는 순수하게 비즈니스 로직만 두고 싶다
  • 그럼 어떻게 해야 될까?
  • 여기서 프록시라는 개념이 등장하게 된다.

 

프록시 적용 전 후 비교

프록시 적용전

 

프록시 적용후

  • 프록시를 도입 하게 되면 프록시에 트랜잭션 처리 로직만 두게 된다.
  • 프록시 로직 안에 내부 적으로 비즈니스 로직을 참조하게 된다.

 

  • 프록시를 적용 하려면 간단하게 스프링 에서 제공해 주는
  • @Transactional 어노테이션을 붙여 주면된다.
 private final MemberRepositoryV3 memberRepositoryV3;


@Transactional
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
    bizLogic(fromId, toId, money);
}

private void bizLogic(String fromId, String toId, int money) throws SQLException {
    Member fromMember = memberRepositoryV3.findById(fromId);
    Member toMember = memberRepositoryV3.findById(toId);

    memberRepositoryV3.update(fromId, fromMember.getMoney() - money);
    validation(toMember);
    memberRepositoryV3.update(toId, toMember.getMoney() + money);
}

 

해결된 부분들

  • 트랜잭션 로직과 비즈니스 로직이 분류가 되었다.

 

참고:

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/unit/110095?tab=curriculum 

 

학습 페이지

 

www.inflearn.com

 

728x90
반응형

'Spring > Spring DB 스터디' 카테고리의 다른 글

[Spring DB] 트랜잭션  (0) 2022.07.31
[Spring DB] 커넥션 풀과 데이터 소스  (0) 2022.07.27

댓글