public interface PlatformTransactionManager {
// 트랜잭션 시작
TransactionStatus getTransaction(TransactionDefinition definition);
// 트랜잭션 커밋
void commit(TransactionStatus status);
// 트랜잭션 롤백
void rollback(TransactionStatus status);
}
@Service
public class UserService {
@Transactional
public void createUser(User user) {
// 이 메서드는 트랜잭션으로 처리됨
userRepository.save(user);
emailService.sendWelcomeEmail(user);
}
}
@Service
public class UserService {
private final PlatformTransactionManager transactionManager;
public void createUser(User user) {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
userRepository.save(user);
emailService.sendWelcomeEmail(user);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
// 기본값: 트랜잭션이 없으면 새로 생성, 있으면 참여
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processPayment(Payment payment) {
// 항상 새로운 트랜잭션 생성
}
@Transactional(propagation = Propagation.SUPPORTS)
public Order findOrder(Long id) {
// 트랜잭션이 있으면 참여, 없으면 트랜잭션 없이 실행
}
}
REQUIRED (기본값)
REQUIRES_NEW
SUPPORTS
MANDATORY
NEVER
NOT_SUPPORTED
@Transactional(isolation = Isolation.READ_COMMITTED)
public void processOrder(Order order) {
// READ_COMMITTED 격리 수준으로 실행
}
상세 내용은 아래 링크글 참고 부탁드립니다.
트랜잭션 격리수준의 이해
// 실제 생성되는 프록시의 동작 방식
public class UserServiceProxy extends UserService {
private final PlatformTransactionManager transactionManager;
private final UserService target;
@Override
public void createUser(User user) {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
target.createUser(user); // 실제 메서드 호출
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
public class TransactionSynchronizationManager {
private static final ThreadLocal<Map<Object, Object>> resources = new ThreadLocal<>();
// 트랜잭션 리소스(Connection) 바인딩
public static void bindResource(Object key, Object value) {
Map<Object, Object> map = resources.get();
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
map.put(key, value);
}
// 트랜잭션 리소스 조회
public static Object getResource(Object key) {
Map<Object, Object> map = resources.get();
if (map == null) {
return null;
}
return map.get(key);
}
}
Q: @Transactional이 동작하는 원리에 대해 설명해주세요.
A: @Transactional은 Spring AOP를 기반으로 동작합니다. Spring은 @Transactional이 붙은 클래스나 메서드에 대해 프록시를 생성합니다. 이 프록시는 실제 메서드 호출 전후에 트랜잭션 관련 로직을 추가하여 실행합니다. 구체적으로는 트랜잭션 시작, 커밋, 롤백 등의 처리를 담당하며, TransactionManager를 통해 실제 트랜잭션을 관리합니다. 또한 ThreadLocal을 사용하여 트랜잭션 정보를 전파하고 관리합니다.
Q: 트랜잭션 전파 속성 중 REQUIRED와 REQUIRES_NEW의 차이점은 무엇인가요?
A: REQUIRED와 REQUIRES_NEW는 다음과 같은 차이가 있습니다:
Q: 스프링의 트랜잭션 추상화가 제공하는 이점은 무엇인가요?
A: 스프링의 트랜잭션 추상화는 다음과 같은 이점을 제공합니다:
1. 데이터 액세스 기술에 독립적인 코드 작성 가능 (JPA, JDBC, Hibernate 등)
2. 선언적 트랜잭션과 프로그래밍 방식 트랜잭션을 모두 지원
3. 다양한 트랜잭션 매니저를 일관된 방식으로 사용 가능
4. 트랜잭션 전파와 격리 수준을 쉽게 제어 가능
이러한 추상화를 통해 개발자는 실제 트랜잭션 처리 방식에 구애받지 않고 비즈니스 로직에 집중할 수 있습니다.