JPA의 트랜잭션


트랜잭션의 특징, ACID

Atomicity(원자성)

부분적인 성공을 허용하지 않고, All or Nothing
실패 시 모두 실패, 성공 시 모두 성공한다.

Consistency(일관성)

데이터의 정합성을 뜻한다

Isolation(독립성)

트랜잭션 내의 데이터에 대해서는 다른 트랜잭션으로부터 독립적이다.

Durability(지속성)

데이터는 영구적으로 보관된다.


CheckedException

Exception 클래스가 CheckedException이다. 명시적으로 Exception처리를 해주어야한다.

Transaction내에서 Exception이 발생하여도 rollback되지 않고 commit된다.

  • 개발자가 해당 예외처리에 대한 책임을 가지게 된다.
  • catch 구문에서 rollback을 명시적으로 등록해주어야한다.

소스 코드 확인해보기

package org.springframework.transaction.interceptor의 추상 클래스인 TransactionAspectSupport를 찾아보았다.

invokeWithinTransaction() 메서드를 확인해보면 아래와 같이 구성되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}

Exception이 발생하면 catch 구문에 들어가게 되고 completeTransactionAfterThrowing() 메서드가 실행된다.

Transaction의 속성을 rollbackOn()으로 체크하게 되고, true일 시 rollback()을, 아닐 결우에는 commit을 하게 된다.

1
2
3
4
5
6
7
8
9
10
11
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
/* ... */
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}

rollbackOn()메서드는 RuntimeExceptionError 타입을 잡아낸다.

1
2
3
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}

CheckedException에서 Rollback을 하는 방법

@Transactional의 소스 코드를 확인해보자. (org.springframework.transaction.annotation.Transactional)

180번 라인에 rollbackFor라는 클래스가 있다.

1
Class<? extends Throwable>[] rollbackFor() default {};

아래와 같이 명시해주면 Exception클래스가 rollbackOn에 들어가게 되고, rollback 메서드가 수행된다.

1
@Transactional(rollbackfor = Exception.class)

동일한 클래스 내에서 @Transactional 메서드 호출

1
2
3
4
5
6
7
8
9
10
11
12
public class TestClass{

public void test(){
this.testTransaction();
}

@Transactional
public void testTransaction(){
/* ... */
}

}

위와 같이 코드를 작성하고, test()를 호출하면 에러가 발생해도 @Transactional이 없는 것과 같이 rollback이 되지 않고 commit이 된다.

이는 Spring AOP와 관련이 있다. Spring AOP는 프록시 기반으로 Bean 외부에서 메서드가 호출될 때 Annotation를 인식한다.

위 코드는 외부가 아닌 내부에서 호출을 하여 @Transactional이 동작하지 않는 것이다.

참고 : Spring @Transaction method call by the method within the same class, does not work?

Author

Inwoo Jeong

Posted on

2021-12-03

Updated on

2021-12-03

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.

댓글