@Service
public class AccountServiceImpl implements AccountService {
@Transactional(rollbackFor = Exception.class)
public void transfer(String from, String to, int amount) throws AuditException {
// ...
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logFailure(String message) {
// 바깥이 롤백돼도 이 로그는 남는다
}
}이 레슨의 Tx.Transactional 과 Tx.proxy() 는 위 코드가 스프링 내부에서 어떤 순서로 처리되는지를 최소 구현으로 보여 준 것입니다.
| 방법 | 코드 위치 | 비고 |
|---|---|---|
| 자기 프록시 주입 | 같은 클래스, self 필드 |
이 레슨이 쓰는 방식 |
AopContext.currentProxy() |
같은 클래스 | exposeProxy=true 필요 |
| 메서드를 별도 빈으로 분리 | 다른 클래스 | 가장 명확하지만 클래스 증가 |
// MyBatis: 조회한 버전과 다르면 0건 갱신 -> 충돌로 판단
int updated = accountMapper.updateWithVersion(id, newBalance, seenVersion);
if (updated == 0) {
throw new OptimisticLockException("다른 트랜잭션이 먼저 수정했습니다");
}비관적 락처럼 대기하지 않는 대신, 실패 시 재시도 로직을 호출자가 준비해야 합니다.