트랜잭션?
데이터베이스의 상태를 변화시키기 위해 수행하는 작업의 단위를 뜻한다.
•
데이터베이스의 상태를 변화시킨다는 것의 의미?
→ 아래의 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
•