저번에 데이터베이스의 트랜잭션에 대하여 공부를 했었는데요.
앞서 말한대로, 이번에는 스프링에서의 트랜잭션에 대해 개념을 공부하고 코드와 함께 봐보겠습니다.
https://coding-jun.tistory.com/2
트랜잭션이란 ?
위 링크를 잘 안보셨을 분들을 위하여 짧게 나마 설명을 드리자면,
데이터베이스에 SELECT,INSERT,UPDATE,DELETE 문 등의 쿼리를 날림으로써
데이터베이스의 상태를 변화시키는 작업의 단위를 일컫습니다.
( 나머지는 위 게시글에 정리가 되어있으므로 생략하겠습니다 ! )
스프링 에서의 트랜잭션 관리
스프링 에서의 트랜잭션 관리 방법은 2가지가 있습니다.
바로, 프로그램에 의한 트랜잭션 관리와 선언적 트랜잭션 관리가 있습니다.
프로그램에 의한 트랜잭션 관리
@Autowired private PlatformTransactionManager transactionManager;
public void operateSome() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
} catch (RuntimeException e) {
transactionManager.rollback(status);
throw e;
} finally {
if (status.isRollbackOnly()) {
transactionManager.rollback(status);
} else {
transactionManager.commit(status);
}
}
}
위의 코드와 같이 트랜잭션 매니저를 통해서 직접 트랜잭션 개시, 커밋, 롤백등을 수행하는 방법으로 소스코드에 직접기술하기 때문에 가독성을 떨어트리고 실수할 가능성도 높아지므로 많이 사용하는 방식은 아닙니다만, 내부구현을 외부에 노출하고 싶지 않은 경우에 사용할 수 있습니다.
선언적 트랜잭션 관리
트랜잭션에 관한 코드를 비지니스 코드로 부터 분리해서 비침투적인 방법으로 기술하여 관리하는 방법을 의미합니다.
@Transactional
public class SomeService() {
@Transactional
public void operateSome() {
}
}
@Transactional 어노테이션을 사용할 경우에는 프로그램을 사용할 때와 같이 트랜잭션 매니저를 직접 지정하지 못하기 때문에,
transactionManager라는 속성을 통해서 사용할 트랜잭션 매니저를 지정할 수 있습니다.
AOP 설정을 이용하여 트랜잭션을 관리할 수도 있습니다.
트랜잭션관리의 대부분은 선언적 트랜잭션 관리로 사용됩니다.
JAVA 트랜잭션에 관하여 알려진 잘못된 정보
여러분들 혹시 자바를 공부하실때 CheckedException 과 UnCheckedException 에 대해 공부해보신적이 있나요?
위 키워드를 구글링 해보면
위와 같은 사진을 보셨을 겁니다.
여기서 잘못된 부분은 바로 예외발생시 트랜잭션 처리 입니다.
위 표는 "ChekedException 이 발생하면 roll-back을 하지 않음" 과 "UnchekdException 이 발생하면 roll-back을 함" 라고 정의하고 있습니다.
ChekedException 과 UnchekdException 은 rollback에 대한 규칙이 정해져있지 않습니다.
roll-back을 정하는 것은 개발자들이 정하는겁니다.
그렇다면 저 표에서 예외발생시 트랜잭션 처리 문장은 어디서 기원된걸까요?
바로 스프링 트랜잭션 공식문서에서 기원되었습니다.
스프링의 트랜잭션 처리는 위에서 말한것과 같이 CheckedException 이 발생하면 roll-back을 하지 않고, UnchekdException 이 발생하면 roll-back을 하게 되어있습니다.
( 이 내용은 어디까지나 default 값이며, @Transactional 의 속성으로 설정 할 수 있습니다. )
Spring 트랜잭션 속성
속성 | 설명 |
propagation | 트랜잭션 개시할지 등 전파행위에 관한 속성. |
isolation | 트랜잭션 격리레벨에 관한 속성으로 기본값은 Default 레벨이며 실제 사용하는 데이터베이스(JDBC) 등의 기본값을 따릅니다. |
readOnly | 트랜잭션을 읽기전용으로 지정하는 속성. 최적화 관점에서 지원되는 프로퍼티이므로 현재 트랜잭션 상태에 따라 다르게 동작할 수 있습니다. |
timeout | 트랜잭션의 타임아웃(초단위)을 지정하는 속성으로 지정하지 않을 경우 사용하는 트랜잭션 시스템의 타임아웃을 따릅니다. |
rollbackFor | Checked 예외 발생시에 롤백을 수행할 예외를 지정하는 속성. |
rollbackForClassName | rollbackFor와 동일하지만 문자열로 클래스명을 지정하는 속성. |
noRollbackFor | Spring의 트랜잭션은 기본적으로 Runtime예외만 롤백처리를 수행하지만 Runtime예외중 특정 예외는 롤백을 수행하지 않아야 할 경우 사용하는 속성. |
noRollbackForClassName | noRollbackFor와 동일하지만 문자열로 클래스명을 지정하는 속성. |
propagation( 전파행위 속성 )
전파행위(거동)는 트랜잭션을 개시할지 혹은 기존 트랜잭션을 이용할지 등 트랜잭션 경계(Transaction Boundary)를 설정할 때 이용하는 속성으로 가장 중요한 속성이라고 할 수 있습니다.
@Transactional(propagation = Propagation.XXX)
- REQUIRED (Default) : 이미 진행 중인 트랜잭션이 있다면 해당 트랜잭션 속성을 따르고, 진행중이 아니라면 새로운 트랜잭션을 생성시킵니다.
- REQUIRES_NEW : 항상 새로운 트랜잭션을 생성한다. 이미 진행중인 트랜잭션이 있다면 잠깐 보류하고 해당 트랜잭션 작업을 먼저 진행합니다.
- SUPPORT : 이미 진행중인 트랜잭션이 있다면 보류하고, 트랜잭션 없이 작업을 수행합니다.
- MANDATORY : 이미 진행중인 트랜잭션이 있어야만, 작업을 수행합니다. 없다면 Exception을 발생시킵니다.
- NEVER : 트랜잭션이 진행 중이지 않을 때 작업을 수행합니다. 트랜잭션이 있다면 Exception을 발생시킵니다.
- NESTED : 진행 중인 트랜잭션이 있다면 중첩된 트랜잭션이 실행되며, 존재하지 않으면 REQUIRED와 동일하게 실행됩니다.
isolation( 격리 속성 )
격리속성은 데이터베이스의 격리 수준의 개념을 따릅니다.
@Transactional(isolation=Isolation.XXX)
- DEFAULT : 기본 격리 수준 (기본이며, DB의 isolation level을 따릅니다.)
- READ_UNCOMMITED (level 0) : 커밋되지 않는 데이터에 대한 읽기를 허용어떤 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 B라는 아직 완료되지 않은(Uncommitted 혹은 Dirty) 데이터 B를 읽을 수 있습니다. (Dirty Read 발생)
- READ_COMMITED (level 1) : 커밋된 데이터에 대해 읽기 허용어떠한 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 해당 데이터에 접근할 수 없습니다. (Dirty Read 방지)
- REPEATEABLE_READ (level 2) : 동일 필드에 대해 다중 접근 시 모두 동일한 결과를 보장트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정이 불가능하다.
선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 후행 트랜잭션이 갱신하거나 삭제가 불가능하기 때문에 같은 데이터를 두 번 쿼리 했을 때 일관성 있는 결과를 리턴합니다. (Non-Repeatable Read 방지) - SERIALIZABLE (level 3) : 가장 높은 격리, 성능 저하의 우려가 있음데이터의 일관성 및 동시성을 위해 MVCC(Multi Version Concurrency Control)을 사용하지 않습니다.
(MVCC는 다중 사용자 데이터베이스 성능을 위한 기술로 데이터 조회 시 LOCK을 사용하지 않고 데이터 버전을 관리해 데이터의 일관성 및 동시성을 높이는 기술)
트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정 및 입력이 불가능합니다. (Phantom Read 방지)
격리 수준에 대해 궁금하시다면!
https://coding-jun.tistory.com/2
아래 글을 참고해주세요!
readOnly ( 읽기 전용 )
@Transactional(readonly = true)
readOnly 를 true 로 설정하면
true시 insert, update, delete 실행 시 예외 발생가 발생됩니다.
readOnly 의 옵션을 true 로 설정해주면 성능향상에 도움이 되는데요?
그 이유는 JPA 의 구현체인 하이버네이트에서 Session Flush Mode를 FlushMode.MANUAL로 설정하게됩니다.
MANUAL로 설정이 되면 강제로 플러시를 호출하지 않는 한 플러시가 일어나지 않게 됩니다.
쉽게 말하자면, 트랜잭션을 커밋하여도 영속성 콘텍스트가 플러시 되지 않아서 엔티티의 등록, 수정, 삭제가 동작하지 않으며 추가적으로 영속성 콘텍스트는 변경 감지를 위한 스냅숏을 보관하지 않으므로 성능이보다 향상이 됩니다.
timeOut ( 타임 아웃 )
@Transactional(timeout=10)
timeOut 으로 시간을 지정해두면 지정해둔 시간만큼 메소드가 수행이 완료되지 않을경우 rollback 을 수행합니다.
( timeOut 의 default 값은 -1 로 따로 설정을 하지 않은 것입니다. )
rollbackFor ( 예외 추가 )
@Transactional(rollbackFor=Exception.class)
특정 예외를 추가하여 발생시 rollback을 처리하는 속성입니다.
noRollbackFor( 예외무시 )
@Transactional(rollbackFor=Exception.class)
특정 예외가 발생시 rollback을 처리 하지 않는 속성입니다.
( 위에서 말한것처럼 rollback을 하는것은 개발자가 정할 수 있습니다. )
'스프링' 카테고리의 다른 글
[스프링] Kotlin + JPA 를 사용하면서 생긴 이슈 ( No-args ) (0) | 2022.07.26 |
---|
댓글