BackEnd/Spring
[Spring] @Transactional 제대로 알고 사용하기
꾹꾹이
2022. 10. 3. 20:03
728x90
트랜잭션이란?
비즈니스 로직에서 쪼갤 수 없는 하나의 작업 단위이다.
데이터베이스의 상태를 변경하며 한 번에 수행되어야 한다.
begin, commit 을 자동으로 수행해주며, exception 발생 시 자동 rollback 처리해준다.
처리 방법
스프링에서 어노테이션 방식으로 메소드, 클래스, 인터페이스 위에 추가하여 사용한다. 이 방식을 선언적 트랜잭션이라고 부르며, 적용된 범위에서는 트랜잭션 기능이 포함된 프록시 객체가 생성되어 자동으로 commit / rollback 시켜준다.
@Transactional 옵션
1. isolation
트랜잭션에서 일관성없는 데이터 허용 수준을 설정
2. propagation
트랜잭션 동작 도중 다른 트랜잭션을 호출할 때, 어떻게 할 것인지 지정하는 옵션
3. noRollbackFor
특정 예외 발생 시 rollback 하지 않음
4. rollbackFor
특정 예외 발생 시 rollback 함
5. timeout
지정한 시간 내에 메소드 수행이 완료되지 않으면 rollback. (-1일 경우 timeout을 사용하지 않음)
6. readOnly
트랜잭션을 읽기 전용으로 설정
1. isolation(격리레벨)
1
2
3
4
|
@Transactional(isolation=Isolation.DEFAULT)
public void addUser(UserDTO dto) throws Exception {
...
}
|
cs |
- DEFAULT: 기본 격리 수준. 기본이며, DB의 Isolation Level을 따른다.
- READ_UNCOMMITED(level 0): 커밋되지 않은 데이터에 대한 읽기를 허용. => Dirty Read 발생
- READ_COMMITED(level 1): 커밋된 데이터에 대해 읽기 허용. => Dirty Read 방지
- REPEATEABLE_READ(level 2): 동일 필드에 대해 다중 접근 시 모두 동일한 결과를 보장. 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 데이터 수정이 불가능하다. 선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 후행 트랜잭션이 갱신하거나 삭제가 불가능 하기 때문에 같은 데이터를 두 번 쿼리했을 때 일관성 있는 결과를 리턴한다. => Non-Repeatable Read 방지
- SERIALIZABLE(level 3): 가장 높은 격리, 성능 저하. 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리고 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정 및 입력이 불가능하다. => Phantom Read 방지
Dirty Read: 트랜잭션 1이 수정중인 데이터를 태랜잭션 2가 읽을 수 있다. 만약 트랜잭션 1의 작업이 정상 커밋되지 않아 롤백되면, 트랜잭션 2가 읽었던 데이터는 잘못된 데이터가 되는 것이다.
Non-repeatable read: 트랜잭션 1이 회원 A를 조회 중에 트랜잭션 2가 회원 A의 정보를 수정하고 커밋한다면, 트랜잭션 1이 다시 회원 A를 조회했을 때 수정된 데이터가 조회된다. 이전 정보를 다시 조회할 수 없는, 반복해서 같은 데이터를 읽을 수 없는 경우이다.
Phantom read: 트랜잭션 1이 특정 조건의 회원을 조회하고 그 이후에 특정 조건의 회원 데이터가 추가되었다. 트랜잭션 1이 다시 특정 조건의 회원을 조회한다면 회원이 추가된 데이터가 조회된다. 이처럼 반복 조회 시 결과 집합이 달라지는 경우이다.
2.propagation (전파속성)
1
2
3
4
|
@Transactional(propagation=Propagation.REQUIRED)
public void addUser(UserDTO dto) throws Exception {
...
}
|
cs |
- REQUIRED (Defualt): 이미 진행중인 트랜잭션이 있다면 해당 트랜잭션 속성을 따르고, 진행중이 아니라면 새로운 트랜잭션을 생성한다.
- REQUIRES_NEW: 항상 새로운 트랜잭션을 생성한다. 이미 실행 중인 트랜잭션이 있다면 잠깐 보류하고 해당 트랜잭션 작업을 먼저 실행한다.
- SUPPORT: 이미 실행 중인 트랜잭션이 있다면 해당 트랜잭션의 속성을 따르고, 없다면 트랜잭션을 설정하지 않는다.
- NOT_SUPPORT: 이미 실행 중인 트랜잭션이 있다면 보류하고, 트랜잭션 없이 작업을 수행한다.
- MANDATORY: 이미 실행 중인 트랜잭션이 있어야만 작업을 수행한다. 없다면 Exception을 발생시킨다.
- NEVER: 트랜잭션이 실행 중이지 않을 때 작업을 수행한다. 트랜잭션이 있다면 Exception을 발생시킨다.
- NESTED: 실행 중인 트랜잭션이 있다면 중첩된 트랜잭션이 실행되며, 존재하지 않으면 REQUIRED와 동일하게 실행된다.
3. noRollbackFor(예외무시)
특정 예외 발생 시 Rollback 처리하지 않는다.
1
2
3
4
|
@Transactional(noRollbackFor=Exception.class)
public void addUser(UserDTO dto) throws Exception {
...
}
|
cs |
4. rollbackFor(예외추가)
특정 예외 발생 시 강제로 Rollback
1
2
3
4
|
@Transactional(rollbackFor=Exception.class)
public void addUser(UserDTO dto) throws Exception {
...
}
|
cs |
5. timeout(시간지정)
지정된 시간 내에 해당 메소드가 실행완료되지 않으면 rollback 수행
-1일 경우 no timeout(Default = -1)
1
2
3
4
|
@Transactional(timeout=5)
public void addUser(UserDTO dto) throws Exception {
...
}
|
cs |
6. readOnly(읽기전용)
true: insert, update, delete 싫애 시 예외 발생(Default = false)
1
2
3
4
|
@Transactional(readOnly=true)
public void addUser(UserDTO dto) throws Exception {
...
}
|
cs |
모든 예외 처리에 대해서 rollback 하고싶은 경우
rollbackFor = Exception.class 를 사용해야 한다.