프레임워크/Spring

[Spring] @Transactional과 propagation

pythaac 2022. 4. 4. 19:37

1. 상황

  프로젝트 도중 @Transactional을 선언했음에도 불구하고, 해당 메서드가 실행 도중 exception이 발생하였을 때 rollback이 일어나지 않는 문제가 발생하였습니다. 이를 계기로, @Transactional에 사용법에 대해 학습한 내용을 정리하려 합니다. 아래 링크는 해당 이슈에 대한 github 링크입니다.

https://github.com/pythaac/mamacoco/issues/4

 

[Database] TistorySyncExecuter에서 Transaction Rollback 오작동 · Issue #4 · pythaac/mamacoco

Description [Code] TistorySyncExecuter의 각 메서드 [Task] @transactional 메서드에서 Exception 발생 [Error] Transaction rollback이 동작하지 않음 To Reproduce #3 과 같음 deletePost 실패시 삭제된 Post가 rollback되지 않음 Scree

github.com

 

 

2. 트랜잭션의 Propagation

  트랜잭션의 Propagation은 트랜잭션 내에서 트랜잭션이 다시 호출되어 중첩되었을 때, 이를 처리하는 방법을 다루고 있습니다. 제가 확인해본 방식은 REQUIRED, REQUIRES_NEW, MANDATORY, NESTED 총 4가지 입니다.

 

REQUIRED (default)

REQUIRED는 자식 트랜잭션이 부모 트랜잭션으로 합쳐지는 방식입니다. 즉, 부모 트랜잭션이 있다면 부모 트랜잭션만 있는 것과 같습니다.

 

REQUIRES_NEW

REQUIRES_NEW는 새로운 트랜잭션을 생성합니다. 두 트랜잭션은 독립적으로 취급되며, 따라서 COMMIT과 ROLLBACK도 서로 영향을 주지 않습니다.

 

MANDATORY

MANDATORY는 부모 트랜잭션을 필요로 합니다. 부모 트랜잭션이 없다면 exception이 발생합니다.

 

NESTED

NESTED는 중첩된 트랜잭션으로, exception 위치에 따라 ROLLBACK의 정도가 달라집니다. SAVEPOINT와 같은 기능으로, 지정된 상태까지 ROLLBACK합니다.

위 이슈를 다루면서 제가 원하는 결과는 NESTED였고, 여러 Propagation을 테스트해보았지만 REQUIRED만 동작하는 것으로 확인되었습니다. StackOverflow[1]에서는 MySQL이 Nested transaction을 지원하지 않는다고 이야기하고 있고, 5.7 버전 document[2]에서도 트랜잭션이 nested되지 않는다고 나와있으며, 8.0 버전 document[3]에서는 SAVEPOINT가 Nested transaction을 구현할 수 있다고 나와있습니다. 따라서 현재까지 확인한 바로는 MySQL에서 NESTED를 사용할 수 없는 것 같습니다.

 

3. @Transactional 사용시 주의사항

  위 이슈를 다루면서, Transaction이 rollback되지 않고, CrudRepository의 delete 함수에서 ClassCastException이 발생하는 등 여러 문제가 발생하였습니다. 이를 통해서 @Transactional을 사용할 때 필수적으로 알아야할 사항들을 정리해보았습니다.

  1. Spring Bean이 해당 메서드를 호출
  2. public 메서드
  3. 한 클래스에서 @Transactional이 아닌 메서드가 @Transactional 메서드 호출

StackOverflow[6]에 의하면 @Transactional 메서드 호출시 aspect로 감싸 호출하기 때문에 해당 메서드는 public이며 Spring Bean이 호출해야합니다. 또한 Spring bean이 호출한다 해도, 호출하는 메서드에 @Transactional이 없으면 Rollback이 일어나지 않습니다. @Transactional 메서드를 직접 호출하도록 설계해야합니다.

 

 

 

[1] https://stackoverflow.com/questions/1306869/are-nested-transactions-allowed-in-mysql

 

Are nested transactions allowed in MySQL?

Does MySQL allow the use of nested transactions?

stackoverflow.com

[2] https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html

 

MySQL :: MySQL 5.7 Reference Manual :: 13.3.3 Statements That Cause an Implicit Commit

13.3.3 Statements That Cause an Implicit Commit The statements listed in this section (and any synonyms for them) implicitly end any transaction active in the current session, as if you had done a COMMIT before executing the statement. Most of these state

dev.mysql.com

[3] https://dev.mysql.com/doc/refman/8.0/en/glossary.html

 

MySQL :: MySQL 8.0 Reference Manual :: MySQL Glossary

MySQL 8.0 Reference Manual  /  MySQL Glossary These terms are commonly used in information about the MySQL database server. A .ARM file Metadata for ARCHIVE tables. Contrast with .ARZ file. Files with this extension are always included in backups produce

dev.mysql.com

[4] https://deveric.tistory.com/86

 

[Spring] 트랜잭션의 전파 설정별 동작

트랜잭션의 전파 설정이란 Spring에서 사용하는 어노테이션 '@Transactional'은 해당 메서드를 하나의 트랜잭션 안에서 진행할 수 있도록 만들어주는 역할을 합니다. 이때 트랜잭션 내부에서 트랜잭

deveric.tistory.com

[5] https://jsonobject.tistory.com/467

 

Spring Boot, @Transactional 전파 레벨 정리

@Transactional 사용시 주의사항 @Transactional 을 클래스 또는 메써드 레벨에 명시하면 해당 메써드 호출시 지정된 트랜잭션이 작동하게 된다. 단, 조건이 있다. 해당 클래스의 Bean을 다른 클래스의 Bean

jsonobject.tistory.com

[6] https://stackoverflow.com/questions/16522412/transactional-rollback-not-working

 

@transactional rollback not working

Below is what I did, I need to implement rollback, using @transactional annotation, but not working as expected, what else need to be done for proper rollback to happen ?? I want that when the co...

stackoverflow.com

[7] https://blog.naver.com/PostView.naver?blogId=varkiry05&logNo=221617256419 

 

[spring] @Transactional 작동 안할때 확인해봐야 할 것

인터넷에 나와있는 각종 설정을 해봐도 안될경우 다음을 참조하면서 점검해보자 1. 메서드가 public 인지 ...

blog.naver.com