////
Search
Duplicate

@Transactional

트랜잭션?

데이터베이스의 상태를 변화시키기 위해 수행하는 작업의 단위를 뜻한다.
데이터베이스의 상태를 변화시킨다는 것의 의미? → 아래의 SQL을 이용하여 데이터베이스에 접근하는 것.
SELECT
INSERT
DELETE
UPDATE
※ 작업의 단위는 질의어 한 문장이 아니다.
작업단위는 여러개의 질의어 명령문들을 사람이 정하는 기준에 따라 정해짐.

트랜잭션의 특징

원자성 (Atomicity)
트랜잭션이 데이터베이스에 모두 반영되던가, 아니면 전혀 반영되지 않아야 한다. → 트랜잭션은 사람이 설계한 논리적인 작업 단위이며, 일처리가 작업단위 별로 이루어 져야 사람이 다루는데 무리가 없다. 만약 트랜잭션 단위로 데이터가 처리되지 않는다면, 설계한 사람은 데이터 처리 시스템을 이해하기 힘들고 오작동 시 원인을 찾기가 힘들어진다.
일관성 (Consistency)
트랜잭션의 작업 처리 결과가 항상 일관성 있어야 한다. → 트랜잭션이 진행되는 동안에 데이터베이스가 변경 되더라도 업데이트된 데이터베이스로 트랜잭션이 진행되는 것이 아니라, 처음 트랜잭션을 진행하기 위해 참조한 데이터베이스로 진행된다. 따라서 각 사용자는 일관성있는 데이터를 볼 수 있음.
독립성 (Isolation)
둘 이상의 트랜잭션이 동시에 실행되고 있을 경우, 어떤 트랜잭션이라도 다른 트랜잭션의 연산에 끼어들 수 없다. → 하나의 특정 트랜잭션이 완료될 때까지, 다른 트랜잭션이 특정 트랜잭션의 결과를 참조할 수 없다.
지속성 (Durability)
트랜잭션이 성공적으로 완료됐을 경우, 결과는 영구적으로 반영되어야 한다.

Commit

하나의 트랜잭션이 성공적으로 끝났고, 데이터베이스가 일관성있는 상태에 있을 때, 하나의 트랜잭션이 끝났다라는 것을 알려주기 위해 사용하는 연산
수행했던 트랜잭션이 로그에 저장되며, 후에 수행한 트랜잭션 단위로 Rollback하는 것을 도와준다.

Rollback

하나의 트랜잭션 처리가 비정상적으로 종료되어 트랜잭션의 원자성이 깨진 경우, 트랜잭션을 처음부터 다시 시작하거나 취소시킨다.

@Transactional

클래스나 메서드에 붙여줄 경우, 해당 범위 내 메서드가 트랜잭션이 되도록 보장해준다.

동작원리와 흐름

Spring AOP를 통해 구현되어있다. (어노테이션 기반)
import org.springframework.transaction.annotation.Transactional;
Java
복사
클래스, 메서드에 @Transactional이 선언되면 해당 클래스에 트랜잭션이 적용된 프록시 객체 생성
프록시 객체는 @Transactional이 포함된 메서드가 호출될 경우, 트랜잭션을 시작하고 Commit or Rollback을 수행
예외가 없을 때는 Commit
uncheckedException이 발생하면 Rollback
※ 프록시? 프록시 패턴은 디자인 패턴 중 하나로, 어떤 코드를 감싸주면서 추가적인 연산을 수행하도록 강제 하는 방법이다.
스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다.
서비스 클래스에서 @Transactional을 사용할 경우, 해당 코드 내의 메서드를 호출할 때 영속성 컨텍스트가 생긴다는 뜻이다. 영속성 컨텍스트는 트랜잭션 AOP가 트랜잭션을 시작할 때 생겨나고, 메서드가 종료되어 트랜잭션 AOP가 트랜잭션을 커밋할 경우 영속성 컨텍스트가 flush되면서 해당 내용이 반영된다. 이후 영속성 컨텍스트 역시 종료된다.

주의점

1.
우선순위 @Transactional은 아래와 같은 우선순위를 가지고 있음 클래스 메서드 → 클래스 → 인터페이스 메서드 → 인터페이스 따라서 공통적인 트랜잭션 규칙은 클래스에, 특별한 규칙은 메서드에 선언하는 식으로 구성. 또한, 인터페이스 보다는 클래스에 적용하는 것을 권고
인터페이스나 인터페이스의 메서드에 적용할 수 있음
하지만 인터페이스기반 프록시에서만 유효한 트랜잭션 설정이 된다.
자바 어노테이션은 인터페이스로부터 상속되지 않기 때문에 클래스 기반 프록시 or AspectJ 기반에서 트랜잭션 설정을 인식할 수 없음
2.
트랜잭션의 모드 @Transactional은 Proxy Mode와 AspectJ Mode가 있는데 Proxy Mode가 Default로 설정됨. Proxy Mode는 다음과 같은 경우 동작하지 않는다.
반드시 public 메서드에 적용되어야한다.
Protected, Private 메서드 에서는 선언되어도 에러가 발생하지는 않지만 동작하지도 않는다.
Non-Public 메서드에 적용하고 싶으면 AspectJ Mode를 고려해야 한다.
@Transactional이 적용되지 않은 Public 메서드에서 @Transactional이 적용된 Public 메서드를 호출할 경우 트랜잭션이 동작하지 않는다.
3.
다른 AOP 기능과의 충돌을 고려해야 한다.
예를 들어 @Secured를 통해 권한이 있는 사용자 여부를 확인하는데 @Transactional이 먼저 수행된다면 권한 검사가 무의미해진다.
이를 방지하기 위해서는 @Order를 이용해 적용 순서를 정하거나 적용범위를 조정해서 해결할 수 있다.
4.
Service 계층에서 사용하자
Service 계층에서 @Transactional을 사용하므로써 여러 데이터베이스 작업들을 원자적으로 처리할 수 있다.
Spring에서 단일 책임원칙에 따라 Database계층에서 비즈니스 로직과 관련없는 역할을 담당해 코드의 유지보수와 확장성을 높이기 위함.
5.
Exception을 고려하자
트랜잭션은 RuntimeException과 Error에는 롤백되지만, Checked exceptions에서는 롤백되지 않는다.
Checked exceptions는 예측가능한 에러를 말하고, 아래와 같이 @Transactional에 rollbackFor 속성을 두어 롤백처리가 되도록 할 수도 있다.
@Transactional(rollbackFor={Exception.class})
Java
복사

@Transactional 속성

@Transactional 어노테이션에는 여러가지 속성이 있다. 그 중에서도 대표적인 속성인 propagation과 isolation에 대해 알아보았다.

propagation

propagation은 여러 트랜잭션이 관련된 경우 트랜잭션 전파 방식을 결정한다.
REQUIRED
트랜잭션이 있는 경우 참여하고 없으면 새 트랜잭션을 생성한다. propagation 설정이 없는 경우의 기본값.
REQUIRES_NEW
항상 새 트랜잭션을 만들고 트랜잭션이 있다면 끝날 때까지 일시중지한다.
NESTED
기존 트랜잭션과 중첩된 트랜잭션을 생성하고, 없다면 새로 트랜잭션을 생성한다.
SUPPORTS
존재하는 트랜잭션이 있다면 지원하고, 없으면 트랜잭션 없이 메서드만 실행한다.
MANDATORY
반드시 트랜잭션이 존재해야 하는 유형으로 없으면 예외(ThrowIllegalTransactionStateException)가 발생한다.
NOT_SUPPORTED
트랜잭션이 있어도 중단되며, 트랜잭션을 지원하지 않는다.
NEVER
트랜잭션이 존재하면 예외(ThrowIllegalTransactionStateException)가 발생한다.
@Transactional(propagation = Propagation.REQUIRED) // Default @Transactional(propagation = Propagation.REQUIRES_NEW) @Transactional(propagation = Propagation.NESTED) @Transactional(propagation = Propagation.SUPPORTS) @Transactional(propagation = Propagation.MANDATORY) @Transactional(propagation = Propagation.NOT_SUPPORTED) @Transactional(propagation = Propagation.NEVER)
Java
복사
그래서 트랜잭션 전파방식을 어떤 경우에 설정하는지??

isolation

트랜잭션의 격리수준을 설정하며, 속성을 설정하지 않은 경우에는 기본값으로 연결된 DB의 격리수준을 따른다.
READ_UNCOMMITTED
가장 낮은 격리수준으로 다른 트랜잭션에서 commit되지 않은 상태의 데이터까지 읽어온다. (Dirty read)
READ_COMMITTED
UNCOMMITTED와 반대로 commit된 내용만 읽어온다. 하지만 트랜잭션들이 동시에 수행되고 있다면 commit 이후의 데이터가 다른 동시성 문제가 발생할 수 있다. (Nonrepeatable read)
REPEATABLE_READ
하나의 트랜잭션에 하나의 스냅샷을 이용하기 때문에 READ_COMMITTED와 같은 문제가 발생하지 않지만 다시 데이터를 조회하는 과정에서 새로 추가되거나 제거된 값을 가져올 수 있다. (Phantom read)
SERIALIZABLE
가장 높은 격리수준으로 READ시에 DML 작업이 불가능하기 때문에 동시성이 낮다. ※ DML작업 : Data Manipulation Language는 데이터베이스에서 데이터를 조작하는데 사용되는 언어를 의미한다. 주로 SELECT, INSERT, UPDATE, DELETE 등의 SQL 명령어를 포함한다.
@Transactional(isolation = Isolation.READ_UNCOMMITTED) @Transactional(isolation = Isolation.READ_COMMITTED) @Transactional(isolation = Isolation.REPEATABLE_READ) @Transactional(isolation = Isolation.SERIALIZABLE)
Java
복사
그래서 언제 트랜잭션의 격리수준은 어떤 경우에 설정하는데??
추가로 알아볼 내용..
@Transactional과 Lazy Loading