모르지 않다는 것은 아는것과 다르다.

Spring

Transactional Propagation

채마스 2022. 2. 26. 02:23

Transaction Propagation 종류

  • REQUIRED (default)
    • 기존에 시작된 트랜잭션이 있으면 유효성을 검사해서 문제가 있다면 Exception 을 던지고 문제가 없으면 기존의 시작된 트랜잭션을 사용한다.
    • 만약 기존에 시작된 트랜잭션이 없다면? -> 새로운 트랜잭션을 생성한다.
    • 실무에서는 거의 이 옵션을 주로 사용한다.
  • REQUIRES_NEW
    • 부모 트랜잭션이 있더라도 항상 새로운 트랜잭션을 시작한다.
    • 이미 진행중인 트랜잭션이 있으면 트랜잭션을 잠시 보류시킨다.
    • 그리고 자식 트랜잭션이 완료(커밋 혹은 롤백)되면 보류되었던 트랜잭션이 다시 활성화된다.
  • SUPPORTS
    • 기존에 시작된 트랜잭션이 있으면 유효성을 검사해서 문제가 있다면 Exception 을 던지고 문제가 없으면 기존의 시작된 트랜잭션을 사용한다.
    • 만약 기존에 시작된 트랜잭션이 없다면? -> 새로운 트랜잭션을 생성하지 않는다.
    • 기존의 트랜잭션이 있다면? -> 기존 트랜잭션에 참여한다.
  • NESTED
    • 중첩된 트랜잭션은 먼저 시작된 부모 트랜잭션의 커밋과 롤백에 결과에 따라 자식 트랜잭션이 영향을 받는다.
    • 하지만 자식의 커밋과 롤백은 부모 트랜잭션에게 영향을 주지 못한다.
    • 예를들어 부모의 로그 트랜잭션이 롤백되면 자식의 로그 트랜잭션도 같이 롤백되지만, 반대로 자식의 로그 트랜잭션이 롤백돼도 부모의 로그 작업에 이상이 없다면 부모 트랜잭션은 정상적으로 커밋된다.
  • MANDATORY
    • 기존에 시작된 트랜잭션이 있으면 유효성을 검사해서 문제가 있다면 Exception 을 던지고 문제가 없으면 기존의 시작된 트랜잭션을 사용한다.
    • 만약 기존에 시작된 트랜잭션이 없다면? -> Exception 을 던진다.
    • 혼자서는 독립적으로 트랜잭션을 진행하면 안되는 경우에 사용한다.
  • NOT_SUPPORTED
    • 기존에 트랜잭션이 있으면 중단하고, 트랜잭션을 생성하지 않은 상태에서 비즈니스 로직을 수행합니다.
  • NEVER
    • 기존에 트랜잭션이 있으면 Exception을 던집니다.
    • 트랜잭션을 사용하지 않도록 강제합니다.





코드 예시

REQUIRED (default)

  • 자식이 문제인 경우
// 부모 트랜잭션
@Transactional(rollbackFor=Exception.class)
public void parentLogic() { 
    parent.create(new Parent("엄마"));
    childLogic(new Child("자식")); // 자식 트랜잭션 요청
    parent.create(new Parent("엄마"));
}

// 자식 트랜잭션
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public void childLogic(String name) { 
    try {
        child.create(name);
        throw new RuntimeException("자식이 문제");
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}
  • 부모는 멀쩡한데 자식이 문제가 생겼을 경우다.
  • REQUIRED 의 핵심은 의리 이다.
  • 자식이 문제가 생기는 경우 부모가 다 정상수행을 했다 하더라도 모두 롤백이 된다.
  • 부모가 문제인 경우
// 부모 트랜잭션
@Transactional(rollbackFor=Exception.class)
public void parentLogic() { 
    parent.create(new Parent("엄마"));
    childLogic(new Child("자식")); // 자식 트랜잭션 요청
    parent.create(new Parent("엄마"));
    throw new RuntimeException("부모가 문제");
}

// 자식 트랜잭션
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public void childLogic(String name) { 
    try {
        child.create(name);
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}
  • REQUIRED 의 핵심은 의리 이다.
  • 부모가 문제가 생겼을 때에도 모두 롤백된다.





REQUIRES_NEW

  • 자식이 문제인 경우
// 부모 트랜잭션
@Transactional(rollbackFor=Exception.class)
public void parentLogic() { 
    parent.create(new Parent("엄마"));
    childLogic(new Child("자식")); // 자식 트랜잭션 요청
    parent.create(new Parent("엄마"));
}

// 자식 트랜잭션
@Transactional(propagation=Propagation.REQUIRED_NEW, rollbackFor=Exception.class)
public void childLogic(String name) { 
    try {
        child.create(name);
        throw new RuntimeException("자식이 문제");
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}
  • REQUIRES_NEW 의 핵심은 각개전투이다.
  • 부모와 자식간의 영향은 없다.
  • 위의 경우 처럼 자식에게만 문제가 있는 경우는 부모는 정상적으로 커밋되고 자식은 로백된다.
  • 부모가 문제인 경우
// 부모 트랜잭션
@Transactional(rollbackFor=Exception.class)
public void parentLogic() { 
    parent.create(new Parent("엄마"));
    childLogic(new Child("자식")); // 자식 트랜잭션 요청
    parent.create(new Parent("엄마"));
    throw new RuntimeException("부모가 문제");
}

// 자식 트랜잭션
@Transactional(propagation=Propagation.REQUIRED_NEW, rollbackFor=Exception.class)
public void childLogic(String name) { 
    try {
        child.create(name);
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}
  • 위의 경우처럼 부모에서만 문제가 있는 경우 부모는 롤백, 자식은 커밋된다.





NESTED

  • 자식이 문제인 경우
// 부모 트랜잭션
@Transactional(rollbackFor=Exception.class)
public void parentLogic() { 
    parent.create(new Parent("엄마"));
    childLogic(new Child("자식")); // 자식 트랜잭션 요청
    parent.create(new Parent("엄마"));
}

// 자식 트랜잭션
@Transactional(propagation=Propagation.NESTED, rollbackFor=Exception.class)
public void childLogic(String name) { 
    try {
        child.create(name);
        throw new RuntimeException("자식이 문제");
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}
  • NESTED 의 핵심은 체크포인트이다.
  • NESTED 호출 직전까지 일단 저장을 해놓는다.
  • 위의 경우처럼 자식 트랜잭션에서 문제가 발생하면 체크 포인트 까지만 롤백된다.
  • 그렇기 때문에 부모 트랜잭션은 커밋, 자식 트랜잭션은 롤백된다.
  • 부모가 문제인 경우
// 부모 트랜잭션
@Transactional(rollbackFor=Exception.class)
public void parentLogic() { 
    parent.create(new Parent("엄마"));
    childLogic(new Child("자식")); // 자식 트랜잭션 요청
    parent.create(new Parent("엄마"));
    throw new RuntimeException("부모가 문제");
}

// 자식 트랜잭션
@Transactional(propagation=Propagation.NESTED, rollbackFor=Exception.class)
public void childLogic(String name) { 
    try {
        child.create(name);
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}
  • NESTED 의 핵심은 체크포인트이다.
  • 자식 트랜잭션에서 문제가 없으면 그 내용은 부모 트랜잭션의 일부가 된다.
  • 하지만 여기서 부모 트랜잭션에서 문제가 발생한다면? -> 이미 부모 트랜잭션에 일부가 되었으므로 같이 롤백된다.




REFERENCES

'Spring' 카테고리의 다른 글

빈 스코프  (0) 2022.02.26
빈 생명주기와 콜백  (0) 2022.02.26
SpringMVC  (0) 2022.02.26
BindingResult  (0) 2022.02.26
Bean Validation (BindingResult 개념을 먼저 숙지 해야된다.)  (0) 2022.02.26