1. 개요
트랜잭션은 참 다양한 곳에서 의미를 가지고 있다.
데이터베이스에서는 상태를 변화시키기 위해 수행하는 작업의 단위를 뜻한다.
일반적인 컴퓨터 과학에서는 쪼개질 수 없는 업무처리의 단위를 의미하기도 한다.
2. 트랜잭션의 성질
- 원자성 : 한 트랜잭션 내에서 실행한 작업은 하나로 간주한다. 즉 모두 성공 혹은 모두 실패다.
- 일관성 : 트랜잭션은 데이터 인터그리티 만족 등 일관성있는 데이터베이스 상태를 유지한다.
- 격리성 : 동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않게 격리한다.
- 지속성 : 트랜잭션이 성공적으로 실행되면 결과는 항상 저장된다.
3. 개발에서의 트랜잭션
DB 접근이 발생하는 여러 단위 작업들을 의미있는 그룹으로 묶어서 일괄 커밋 또는 일괄 롤백하는 매커니즘을 뜻한다.
- 프레임워크들에서는 다음과 같은 기법들을 사용하거나 지원한다.
-
선언적 트랜잭션 관리 방법 사용
- 어노테이션 방식으로 @Transactional을 선언하여 사용하는 방법이 일반적이며 이를 선언적 트랜잭션이라고 부른다. -
JDBC
- JDBC, MyBatis 등을 사용할 때는DataSourceTransactionManager
를 관리자로 등록한다.
- 이 클래스는 dataSource 프로퍼티를 통해 전달받은 Connection으로 커밋, 롤백을 수행하면서 관리한다. -
JPA 트랜잭션 방식 지원
- JPA 트랜잭션은JpaTransactionManager
를 Bean으로 등록하여 사용한다.
- 이 클래스는 프로퍼티를 통해 전달받은 EntityManagerFactort를 이용해서 트랜잭션 -
JTA 트랜잭션 방식 지원
- 다중 자원에 접근 시 JTA(Java Transaction API)를 사용하는데, JTA 트랜잭션은JtaTransactionManager
를 Bean으로 등록하여 사용한다.
- Bean으로 등록할 때 transactionmanagerName 프로퍼티를 이용해서 JNDI 이름을 설정한다. - Nested Transation 지원
- 복수 DataSource에 대한 트랜잭션 관리 제공
-
Lazy Connection
- Lazy Connection이란 쿼리 호출 시점에 해당 데이터소스만 연결하는 것이다.
- Lazy Connection 적용 시, 여러 Lazy Bean Id를 리스트로 등록하고 미적용 시 일반적인 데이터소스 Bean Id를 리스트로 등록한다. -
타임아웃 속성 지원
- 지정한 시간 내에 해당 메소드 수행이 완료되지 않는다면 롤백을 수행한다. -1이라면 타임아웃이 없다.
- @Transactional(timeout=10)
4. 선언적 트랜잭션
선언적 트랜잭션 방법은 <tx:advice> 태그를 사용하는 방법과 @Transactional 어노테이션을 사용하는 방법이 있다.
4.1. tx 네임스페이스 사용
Bean 설정 파일에서 tx 네임스페이스를 추가하여 트랜잭션 속성을 정의한다.
4.2. 어노테이션 기반 설정
@Transactional 어노테이션은 메소드나 클래스, 인터페이스에 적용되며 속성 설정이 가능하다. (propagation의 디폴트가 REQUIRED)
@Transactional(propagation=Propagation.REQUIRED)
@Transactional @Override public void removeBoard(BoardVO vo) throws Exception { replyDAO.removeAllRepl(vo.boardNo); boardDAO.deleteBoard(vo.boardNo); }
메소드에 설정된 @Transactional이 가장 우선된다. 따라서 공통설정은 인터페이스나 클래스에 적용하고 특별한 설정은 메소드에 적용하면 된다.
5. Transactional 속성 (Propagation - Nested Transaction)
한 트랜잭션 내부에서 중첩된 트랜잭션 처리가 필요할 때 사용하는 기능이다. Spring은 propagation 속성을 통해 Nested Transaction을 지정한다.
Propagation(전파옵션)에 대해 알아보자.
- REQUIRED : 이미 시작된 트랜잭션(부모 트랜잭션)이 있으면 참여하고 없으면 새로 시작한다. (디폴트)
- SUPPORTS : 이미 시작된 트랜잭션이 있으면 참여하고 없으면 트랜잭션 없이 진행한다.
- REQUIRED_NEW : 부모 트랜잭션을 무시하고 항상 새로운 트랜잭션을 시작한다.
- NESTED : 메인 트랜잭션 내부에 중첩된 트랜잭션을 시작한다. 부모 트랜잭션 결과에는 영향을 받지만, 자신의 트랜잭션 결과는 부모에게 영향을 미치지 않는다.
- NOT_SUPPORTED : 트랜잭션을 사용하지 않게 한다. 이미 진행 중인 트랜잭션이 있으면 보류시킨다.
- MANDATORY : REQUIRED와 비슷하게 이미 시작된 트랜잭션이 있으면 참여하지만, 트랜잭션이 시작된 것이 없으면 예외를 발생시킨다. (혼자서 독립적으로 트랜잭션을 진행하면 안되는 경우에 사용)
- NEVER : 트랜잭션을 사용하지 않게 하며 이미 시작된 트랜잭션이 있으면 예외를 발생시킨다.
이러한 옵션을 설정하는 방법은 어노테이션 방식과 xml 방식이 있다.
-
어노테이션 방식 :
@Transactional(propagation=Propagation.NESTED, rollbackFor=Exception.class)
-
xml 방식 :
<tx:method name="insert*" propagation="NESTED" rollback-for="Exception" />
6. Transactional 속성 (Isolation)
격리 레벨에 대해 알아보자.
- DEFAULT : 기본 설정, 기본 격리 수준
- SERIALIZABLE : 가장 높은 격리, 성능 저하 가능성 있음
- READ_UNCOMMITED : 커밋되지 않은 데이터 읽을 수 있음
- READ_COMMITED : 커밋된 데이터에 대해 읽기 허용
- REPEATABLE_READ : 동일 필드에 대한 다중 접근 시 동일 결과 보장