스프링 DB 2편(데이터 접근 활용 기술)

Ch11. 스프링 트랜잭션 전파(활용) - 서비스 계층에 트랜잭션이 없을 때 (커밋, 롤백)

webmaster 2022. 7. 8. 17:25
728x90

서비스 계층에 트랜잭션이 없을 때 - 커밋

/**
 * memberService    @Transactional:OFF
 * memberRepository @Transactional:ON
 * logRepository    @Transactional:ON
 */
@Test
public void outerTxOff_success() {
    //given
    String username = "outerTxOff_success";

    //when
    memberService.joinV1(username);

    //then : 모든 데이터가 정상 저장된다.
    assertTrue(memberRepository.find(username).isPresent());
    assertTrue(logRepository.find(username).isPresent());
}

커밋 내부 동작 과정

  1. MemberService에서 MemberRepository를 호출한다. MemberRepository 에는 @Transactional 애노테이션이 있으므로 트랜잭션 AOP가 작동한다. 여기서 트랜잭션 매니저를 통해 트랜잭션을 시작한다. 이렇게 시작한 트랜잭션을 트랜잭션 B라 하자.
    • 그림에서는 생략했지만, 트랜잭션 매니저에 트랜잭션을 요청하면 데이터소스를 통해 커넥션 con1을 획득하고, 해당 커넥션을 수동 커밋 모드로 변경해서 트랜잭션을 시작한다.
    • 그리고 트랜잭션 동기화 매니저를 통해 트랜잭션을 시작한 커넥션을 보관한다.
    • 트랜잭션 매니저의 호출 결과로 status 를 반환한다. 여기서는 신규 트랜잭션 여부가 참이 된다. 
  2. MemberRepository 는 JPA를 통해 회원을 저장하는데, 이때 JPA는 트랜잭션이 시작된 con1을 사용해서 회원을 저장한다. 
  3. MemberRepository 가 정상 응답을 반환했기 때문에 트랜잭션 AOP는 트랜잭션 매니저에 커밋을 요청한다.
  4. 트랜잭션 매니저는 con1 을 통해 물리 트랜잭션을 커밋한다.
    • 물론 이 시점에 앞서 설명한 신규 트랜잭션 여부, rollbackOnly 여부를 모두 체크한다.

이렇게 해서 MemberRepository 와 관련된 모든 데이터는 정상 커밋되고, 트랜잭션 B는 완전히 종료된다. 이후에 LogRepository를 통해 트랜잭션 C를 시작하고, 정상 커밋한다. 결과적으로 둘 다 커밋되었으므로 Member , Log 모두 안전하게 저장된다.

@Transactional과 REQUIRED

트랜잭션 전파의 기본 값은 REQUIRED 이다. 따라서 다음 둘은 같다.

  • @Transactional(propagation = Propagation.REQUIRED)
  • @Transactional

REQUIRED 는 기존 트랜잭션이 없으면 새로운 트랜잭션을 만들고, 기존 트랜잭션이 있으면 참여한다.

서비스 계층에 트랜잭션이 없을 때 - 롤백

/**
 * memberService    @Transactional:OFF
 * memberRepository @Transactional:ON
 * logRepository    @Transactional:ON Exception
 */
@Test
public void outerTxOff_fail() {
    //given
    String username = "로그예외_outerTxOff_fail";

    //when
    assertThatThrownBy(() -> memberService.joinV1(username))
        .isInstanceOf(RuntimeException.class);
    ;

    //then : 모든 데이터가 정상 저장된다.
    assertTrue(memberRepository.find(username).isPresent());
    assertTrue(logRepository.find(username).isEmpty());
}
  • 사용자 이름에 로그예외 라는 단어가 포함되어 있으면 LogRepository에서 런타임 예외가 발생한다.
  • 트랜잭션 AOP는 해당 런타임 예외를 확인하고 롤백 처리한다.

롤백 내부 동작 과정

  • MemberService 에서 MemberRepository를 호출하는 부분은 앞서 설명한 내용과 같다. 트랜잭션이 정상 커밋되고, 회원 데이터도 DB에 정상 반영된다.
  • MemberService 에서 LogRepository를 호출하는데, 로그 예외라는 이름을 전달한다. 이 과정에서 새로운 트랜잭션 C가 만들어진다

LogRepository 응답 로직

  • LogRepository 는 트랜잭션 C와 관련된 con2를 사용한다. 
  • 로그예외 라는 이름을 전달해서 LogRepository에 런타임 예외가 발생한다. 
  • LogRepository 는 해당 예외를 밖으로 던진다. 이 경우 트랜잭션 AOP가 예외를 받게 된다. 
  • 런타임 예외가 발생해서 트랜잭션 AOP는 트랜잭션 매니저에 롤백을 호출한다. 
  • 트랜잭션 매니저는 신규 트랜잭션이므로 물리 롤백을 호출한다
728x90