2024. 6. 23. 08:58ㆍTIL
✔오늘 배운 중요한 🔑 point
- Spring AOP로 구현된 @Transactional 의 Aspect에서는 PlatfromTransactionalManager를 이용해서 커밋, 롤백 작업을 처리하게 된다.
- @Transactional(propagation = Propagation.REQUIRES_NEW)를 사용해서 부분 롤백이나, 독릭적인 트랜잭션이 필요한 경우에 적용시킬 수 있다.
🎯 오늘 배운 내용
@Transactional 기본 개념
트랜잭션은 데이터베이스의 상태를 변경시키기 위해 수행하는 작업 단위이며 하나의 트랜잭션 내에서 수행된 작업은 모두 성공하거나 모두 실패해야한다는 특징을 가진다
@Transactional 동작 방식
Spring Transaction Abstraction 에서는 트랜잭션을 처리하기 위해 TransactionManager 를 사용,
Spring MVC 환경에서는 PlatfromTransactionManager 가 사용된다
Spring AOP로 구현된 @Transactional 의 Aspect에서는 PlatfromTransactionalManager를 이용해서 커밋, 롤백 작업을 처리하게 된다.
https://javacodehouse.com/courses/spring-boot/lesson-9-spring-transaction-handling/
@Transactional 전파방식
REQUIRED (default)
fun main() {
firstTransactionMethod()
}
@Transactional
fun firstTransactionMethod() {
secondTransactionMethod()
}
fun secondTransactionMethod() {
}
firstTransationMehod()가 부모 트랜잭션이며
부모 트랜잭션 안에 secondTransactionMehod가 있기 때문에 자동으로 secondTransactionMehod가 자식 트랜잭션이 된다.
자식 트랜잭션은 따로 @Transactional 어노테이션을 작성하지 않아도 된다.
REQUIRES_NEW ★ ★ ★ ★ ★ ★ ★
부모 트랜잭션과 자식 트랜잭션을 독립적으로 분리하고 싶을 때 사용한다
- 자식 트랜잭션에서 에러가 발생하더라도 부모 트랜잭션에 영향(Rollback 등)을 주고싶지 않을 때
- 부모 트랜잭션이 커밋되기 전까지 기다리지 않고 자식 트랜잭션을 즉시 커밋하고 싶을 때
- 부모 트랜잭션이 실패하더라도 자식 트랜잭션이 성공했다면 자식 트랜잭션에 대한 결과를 커밋하고 싶을 때
@Transactional
fun congratulationBirthday(members: List<Member>) {
for (member in members) {
member.addPoint(5000)
memberRepository.save(member)
sendEmailService.sendBirthDayMail(member)
}
}
@Transactional
fun sendBirthDayMail(member: Member) {
// ...
if (member.id == 997) {
throw RuntimeException("내가 임의로 발생시킨 에러다!")
}
}
사용자 1000명에게 포인트를 지급하는 로직에서 997번 ID의 회원에서 에러가 발생했다면 전체 1000명에 대한 롤백이 이루어질 것이다. 포인트 등은 롤백을 해도 문제가 없을수 있지만 이미 보내버린 메일은 롤백 할 수 없기 때문에 이러한 상황을 타개하기 위해서 REQUIRES_NEW 방식을 사용한다.
@Transactional
fun congratulationBirthday(members: List<Member>) {
for (member in members) {
member.addPoint(5000)
memberRepository.save(member)
try {
sendEmailService.sendBirthDayMail(member)
} catch(ex: RuntimeExcetion) {
member.minusPoint(5000)
memberRepository.save(member)
// 발생한 예외가 회복된다!!
}
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
fun sendBirthDayMail(member: Member) {
// ...
if (member.id == 997) {
throw RuntimeException("997번째 회원은 못줘!")
}
}
위 코드에서는 @Transactional(propagation = Propagation.REQUIRES_NEW)을 이용해서 전파방식을 설정하고 Try-Catch문으로 예외를 회복시키는 것을 확인할 수 있다.
이렇게 하면 특정 회원에 대한 이메일 전송 실패가 전체 트랜잭션에 영향을 미치지 않게 된다.
NOT_SUPPORTED
- 부모 트랜잭션이 있다면 해당 트랜잭션을 보류하고 트랜잭션이 없는채로 로직을 실행한다.
- 부모 트랜잭션이 없다면 NEVER, SUPPORTS 와 동일하게 트랜잭션이 없는채로 로직을 실행한다.
@DataJpaTest
class MemberServiceTest {
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
fun noTransactionTest() {
// ...
}
}
@DataTest 어노테이션은 기본적으로 모든 메소드에 @Transactional 설정이 되어있는데 특정 테스트에 대해서 @Transactional 없이 테이스트 코드를 작성하고 싶다면 NOT_SUPPORTED를 사용하면 된다.
🤔 어떻게 활용할까?
@Transcatinal의 전파방식에 따라서 함수 간의 트랜잭션 흐름을 제어할 수 있다.
📓 오늘의 한줄
"The only real mistake is the one from which we learn nothing."
- Henry Ford -
'TIL' 카테고리의 다른 글
CDN(Content Delivery Network) (0) | 2024.06.25 |
---|---|
CAP 정리 (0) | 2024.06.24 |
@DataJpaTest 를 이용한 통합 테스트 코드 작성하기 (0) | 2024.06.22 |
@DataJpaTest를 이용해서 단위 테스트 코드 작성하기 (0) | 2024.06.21 |
Projection (0) | 2024.06.20 |