백엔드/SpringBoot

[ 스프링부트 / Spring Boot ] 트랜잭션 전파가 안되는 현상

1. 비즈니스 로직 및 테스트 코드

@Service
@RequiredArgsConstructor
public class BookService {
	private final BookRepository bookRepository;
	private final AuthorRepository authorRepository;	
	...

	@Transactional
	void pubBookAndAuthor(){
		Book book = new Book();
		book.setName("JPA 시작하기");

		bookRepository.save(book);

		Author author = new Author();
		author.setName("martin");

		authorRepository.save(author);

		throw new RuntimeException("오류가 발생123123");
	}
}
@Test
void transactionTest(){
    try {
    	bookService.pubBookAndAuthor();
    } catch (RuntimeException e) {
    	System.out.println(">>> " + e.getMessage());
    }

    System.out.println("books : " + bookRepository.findAll());
    System.out.println("authors : " + authorRepository.findAll());
}

2. 트랜잭션이 발생하지 않는 상황 발생

  • 온라인 강의에 @Transactional에 관한 실습 중 아래와 같은 코드로 테스트 코드를 진행
TransactionAspectSupport.java
try {
    // This is an around advice: Invoke the next interceptor in the chain.
    // This will normally result in a target object being invoked.
    retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
    // target invocation exception
    completeTransactionAfterThrowing(txInfo, ex);
    throw ex;
}
finally {
    cleanupTransactionInfo(txInfo);
}
  • RuntimeException (UnChecked Exception) 이 발생했는데 catch를 타지 않아 롤백이 되지 않음
    • @Transactional(propagation = Propagation.REQUIRED) 변경해봤는데 실패...
    • @Transactional(rollbackFor = RuntimeException.class) 역시나 실패..
    •  JpaRepository에 save는 정상적으로 Transaction이 작동하여 둘의 메서드를 비교(메서드 선언의 차이를 확인)
      @Transactional
      @Override
      public <S extends T> S save(S entity) {
      
      	Assert.notNull(entity, "Entity must not be null.");
      
      	if (entityInformation.isNew(entity)) {
      		em.persist(entity);
      		return entity;
      	} else {
      		return em.merge(entity);
      	}
      }

3. 해결

  • pubBookAndAuthor의 메소드가 default로 선언되어 있는 것이 문제 (public으로 변경)
  • default로 선언된 메소드는 트랜잭션 블록으로 인식을 하지 못하고 있음
  • public 메소드로 변경하니 트랜잭션이 정상 작동, TransactionAspectSupport.java catch 영역도 정상 작동
@Transactional
//void pubBookAndAuthor(){ 문제..로 수정
public void pubBookAndAuthor(){
	Book book = new Book();
	book.setName("JPA 시작하기");

	bookRepository.save(book);

	Author author = new Author();
	author.setName("martin");

	authorRepository.save(author);

	throw new RuntimeException("오류가 발생123123");
}

4. 결론

  • 실습으로 메소드 형식을 변경하면서 발생한 일이지만 이로 인해 지나쳐갈 만한 위험한 상황을 겪어 다행이었다..
  • 트랜잭션 블록을 이용하려면 protected(선언 X)를 쓰지 말고 꼭 public으로 지정하자!