Search
〰️

@Transactional

@Transactional

데이터베이스 트랜잭션은 데이터베이스 관리 시스템 또는 유사한 시스템에서 상호작용의 단위이다.
쉽게 말하면 더 이상 쪼개질 수 없는 최소의 연산이라는 의미가 된다.
@Transactional을 클래스 또는 메소드 레벨에 명시하면 해당 메소드 호출시 지정된 트랜잭션이 작동한다. (선언적 트랜잭션)
해당 클래스의 Bean을 다른 클래스의 Bean에서 호출할 때만 @Transactional을 인지하고 작동하게 된다.
같은 Bean 내에서 @Transactional이 명시된 다른 메소드를 호출해도 작동하지 않는다.
 Spring Framework는 내부적으로 AOP를 통해 해당 어노테이션을 인지하여 프록시를 생성하여 트랜잭션을 자동 관리하기 때문이다.
클래스, 메소드 레벨에 @Transactional이 명시되면, 이 클래스에 트랜잭션 기능이 적용된 프록시 객체가 생성된다.
이 프록시 객체는 PlatformTransactionManager를 사용하여 트랜잭션을 시작하고, 정상 여부에 따라 Commit 또는 Rollback 한다.
@Transactional이 붙은 메서드는 메서드가 포함하고 있는 작업 중에 하나라도 실패할 경우 전체 작업을 취소한다. (롤백)

특성

데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기위한 성질 네가지
원자성(Atomicity)
한 트랜잭션 내에서 실행한 작업들은 하나로 간주한다. 즉, 모두 성공 또는 모두 실패.
일관성(Consistency)
트랜잭션은 일관성 있는 DB 상태를 유지한다. (data integrity 만족 등.)
격리성(Isolation)
동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않도록 격리해야한다.
지속성(Durability)
트랜잭션을 성공적으로 마치면 결과가 항상 저장되어야 한다.

Option

Isolation (격리 수준)

동시에 여러 사용자가 데이터에 접근할 때 어디까지 허용할까? 를 정하는 옵션
트랜잭션의 격리 수준과 데이터의 일관성은 비례한다.
격리 수준이 약할수록 데이터 접근 및 수정이 자유롭지만 일관성이 떨어지고, 격리수준이 강해진다면 데이터의 일관성은 증가한다.

Default

사용하는 데이터베이스의 기본 격리 수준을 따른다.

READ_UNCOMMITTED

한 트랜잭션이 처리중인 커밋되지 않은 데이터를 다른 트랜잭션에서 접근 가능하다.
DB에 커밋하지 않은, (DB에 존재하지 않는) 데이터를 읽는 현상을 Dirty Read라고 한다.
데이터 정합성에 문제가 많아 웬만하면 권장되지 않고, 아예 지원하지 않는 경우도 있다.
Dirty Read가 가능하기 때문에, 잘못된 데이터를 읽을 수 있다.
A 트랜잭션이 데이터 1을 조회하여, 2로 변경하고 아직 커밋하지 않음
B 트랜잭션이 동일한 데이터를 조회해서 2라는 값을 받음 (Dirty Read)
A 트랜잭션에서 오류가 발생해서 데이터를 롤백 (2 → 1)
실제 데이터는 1이지만, B트랜잭션은 2라는 잘못된 데이터를 읽는다.

READ_COMMITTED

트랜잭션은 커밋한 데이터만 읽을 수 있다.
A트랜잭션이 데이터를 변경해도 커밋하기 전이라면 B트랜잭션은 변경되기 전의 데이터를 조회할 수 있다.
이 때, B트랜잭션은 Undo 영역에서 데이터를 가져온다.
매 조회시마다 새로운 스냅샷을 뜨기 때문에, 다른 트랜잭션이 커밋한 후 다시 조회하면 변경된 데이터를 볼 수 있다.
대부분의 데이터베이스 기본 격리 수준이며, REPEATABLE_READ와 함께 가장 많이 사용되는 방식이다.
Non-Repeatable Read 현상이 발생할 수 있다.
트랜잭션에서 조회한 데이터가 트랜잭션이 끝나기 전에 다른 트랜잭션에 의해 변경되면, 다시 읽었을 때 새로운 값이 읽히며 데이터가 불일치하게되는 현상이다.
하나의 트랜잭션 내에서 똑같은 SELECT 쿼리를 실행했을 때, 항상 같은 결과를 가져와야 한다는 REPEATABLE READ 정합성의 정의에 어긋난다.
A트랜잭션이 데이터(row)를 읽음
B트랜잭션이 같은 데이터를 수정하고 커밋
A트랜잭션이 다시 같은 데이터를 읽었는데 데이터가 달라짐

REPEATABLE_READ

하나의 트랜잭션은 하나의 스냅샷만 사용한다.
A트랜잭션이 시작하고 처음 조회한 데이터의 스냅샷을 저장하고, 이후에 동일한 쿼리를 호출하면 스냅샷에서 데이터를 가져옵니다.
따라서, 중간에 B트랜잭션이 새로 커밋해도 A트랜잭션이 조회하는 데이터는 변하지 않는다.
Phantom Read라는 다른 트랜잭션에서 수행한 작업에 의해 안보였던 데이터가 보이는 현상이 발생할 수 있다.
REPEATABLE_READ 격리 수준은 조회한 데이터에 대해서만 Shared Lock이 걸리기 때문에, 다른 트랜잭션이 새로운 데이터를 추가할 수 있다.
A트랜잭션이 조회한 데이터는 0건
B트랜잭션이 새로운 데이터를 추가하고 커밋
A트랜잭션이 같은 쿼리로 다시 조회했더니 B트랜잭션이 추가한 데이터까지 같이 조회됨

SERIALIZABLE

가장 단순하고 엄격한 격리수준이다.
순차적으로 트랜잭션을 진행시키며, 읽기 작업에도 잠금을 걸어 여러 트랜잭션이 동시에 같은 데이터에 접근하지 못한다.
가장 안전하지만 성능 저하가 발생하기 때문에, 극도의 안정성을 필요로 하지 않으면 자주 사용되지 않는다

Propagation (전파레벨)

현재 진행중인 트랜잭션(부모 트랜잭션)이 존재할 때, 새로운 트랜잭션 메소드를 호출하는 경우 어떤 정책을 사용할 지에 대한 정의
기존 트랜잭션 참여
새로운 트랜잭션 생성
non-transactional 상태로 실행 등
트랜잭션은 존재하지만 커밋, 롤백이 되지 않는 상태

REQUIRED

디폴트 속성
부모 트랜잭션 내에서 실행하며(부모 트랜잭션이 존재할 경우 참여), 부모 트랜잭션이 없을 경우 새로운 트랜잭션을 생성한다.
자식 트랜잭션이 문제가 생기는 경우, 부모가 다 정상수행을 했다 하더라도 모두 롤백된다.
반대의 케이스인 부모 트랜잭션이 문제가 생기는 경우에도 모두 롤백된다.

SUPPORTS

이미 시작된 트랜잭션이 있으면 참여하고, 그렇지 않으면 트랜잭션 없이 진행하게 만든다. (non-transactional 상태로 실행)

MANDATORY

부모 트랜잭션이 있으면 참여하고, 없으면 예외 발생
혼자서는 독립적으로 트랜잭션을 진행하면 안 되는 경우에 사용

REQUIRES_NEW

항상 새로운 트랜잭션을 시작한다. (부모 트랜잭션 무시)
이미 진행중인 트랜잭션이 있으면, 트랜잭션을 잠시 보류시킨다.
자식 트랜잭션과 부모 트랜잭션은 각자 따로 커밋, 롤백

NOT_SUPPORTED

non-transactional 상태로 실행
이미 진행 중인 트랜잭션이 있으면 트랜잭션을 잠시 보류시킨다.

NEVER

트랜잭션을 사용하지 않도록 강제한다.
이미 진행 중인 트랜잭션도 존재하면 안된다. 있다면 예외를 발생시킨다. (부모 트랜잭션이 존재한다면 예외 발생)

NESTED

이미 진행중인 트랜잭션과 별개의 중첩 트랜잭션을 시작한다.
중첩 트랜잭션은 트랜잭션 안에 다시 트랜잭션을 만드는 것이다.
독립적인 트랜잭션을 만드는 REQUIRES_NEW와는 다르다.
부모 트랜잭션의 커밋과 롤백에는 영향을 받지만, 자신의 커밋과 롤백은 부모 트랜잭션에게 영향을 주지 않는다.
NESTED의 핵심은 체크포인트이다. NESTED 호출 직전까지 일단 저장한다.
자식 트랜잭션이 문제가 생겨 롤백이 되어도, 다시 체크포인트까지만 롤백이 된다. 따라서 부모는 커밋, 자식은 롤백 처리가 된다.

No-rollback-for

특정 예외가 발생하더라도 롤백되지 않도록 설정
출처: