본문 바로가기
개발/Web

[Spring] @Transactional이 적용되지 않을 경우(롤백이 안되는 이유)

by ynzu🤍 2022. 1. 13.
반응형

 

1. Checked Exception일 경우

사실 이 항목에 대한 설명을 위해 어제 java의 error와 exception에 대해 포스팅을 했다.

Checked Exception이 뭔지 모른다면 먼저 아래 포스팅을 보고 오는 것을 추천!

 

[Java] Error, Checked Exception, Unchecked Exception 비교

자바에서는 예외를 크게 Error와 Exception으로 구분하고, Exception은 RuntimeException 상속 여부에 따라 Checked Exception, Unchecked Exception으로 구분된다. Error 시스템에 비정상적인 상황이 발생한 경..

ynzu-dev.tistory.com


Checked Exception는 예외상황 발생시 롤백처리를 하지 않는다.

Spring의 트랜젝션 처리를 하는 클래스를 살펴보면 completeTransactionAfterThrowing 라는 메소드가 있고, 이 메소드 안엔 롤백을 하기 전에 처리하는 부분이 있다.

if (txInfo.transactionAttribute.rollbackOn(ex)) {
  try {
    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
  } catch( ) {
    
  }
  //생략
}
else {
  try {
    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
  }catch(  ) {
    
  }
  //생략
}


이를 추적해면 아래와 같은 소스를 발견할 수 있는데 RuntimeException이거나 Error인 경우만 롤백을 실행하는 것을 알 수 있다. 스프링프레임워크는 EJB의 관습을 따르기 때문이다.

@Override
public boolean rollbackOn(Throwable ex) {
  return (ex instanceof RuntimeException || ex instanceof Error);
}

 


하지만 checked exception의 경우에도 롤백 시켜야될 상황이 있다면 어떻게 해야할까?

  • 방법 1

@Transactional 속성을 보면 rollbackOn이 있다. 이 속성에 해당하는 exception을 추가해주면 된다. exception은 콤마(,)로 구분하여 여러개 추가할 수 있다. (rollbackFor인 경우도 있음.. 버전에 따라 다른듯?)

@Transactional(rollbackOn = {Exception.class})

 

  • 방법 2

catch 부분에 RuntimeException을 상속받은 클래스를 throw 해주기

try{
	//생략
} catch(InvalidObjectException e){
	throw new TestException(e.getMessage());
} catch(Exception e){
	throw new TestException(e.getMessage());
}

 

public class TestException extends RuntimeException {

    private final ErrorCode errorCode;

    public TestException(String message, ErrorCode errorCode) {
        super(message);
        this.errorCode = errorCode;
    }

    public TestException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.errorCode = errorCode;
    }

    public ErrorCode getErrorCode() {
        return errorCode;
    }

}

 


2. 한 클래스 내 @Transaction이 설정되어있지 않은 메소드에서 @Transaction이 설정된 메소드를 호출할 경우

spring에서 @Transactional은 인스턴스에서 처음으로 호출하는 메서드나 클래스의 속성을 따라가게 되어서 동일한 class안에 상위 메소드에 @Transactional이 없으면 하위에 선언되어 있다하더라도 전이되지 않는다. 
@Transactional 아래와 같이 수정하거나 class 또는 bean을 분리한다.

//수정 전
public class TestService() {

    public void test1() {
        test2();
    }

	@Transactional
	public void test2() {
		//생략
	}
}


//수정 후
public class TestService() {

	@Transactional
	public void test1() {
		test2();
	}

	public void test2() {
		//생략
	}
}



3. 메소드가 private일 경우

private이면 @Transactional이 적용되지 않는다. public으로 변경하자!

 

 

728x90
반응형

댓글