Transaction
개념과 사용 목적
개념
- 물리적으로는 여러 개의 작업이지만 논리적으로는 하나의 작업
- 모든 하위 작업이 성공하거나 실패하는 처리
목적
- 데이터의 무결성을 보장하는 핵심적인 역할
ACID 원칙
Atomicity
- 작업을 더 이상 쪼갤 수 없는 특징
- 모든 작업이 성공하거나 실패하는 특징
Consistency
- 일관성 있게 저장되거나 변경되는 것
Isolation
- 각각의 트랜잭션은 독립적으로 실행
- 다른 트랜잭션에 영향을 주지 않는 것
Durability
- 결과가 지속되는 것
- 데이터가 물리적인 장소에 지속적으로 유지
Transaction Commit & Rollback
Commit
- 모든 작업을 최종적으로 반영하는 명령어
- 변경 내용이 DB에 영구 저장
- 하나의 트랜잭션 종료
Rollback
- 작업 중 문제가 생겼을 때 수행된 작업들 취소
- 트랜잭션 시작 이전 상태로 복귀
Commit & Rollback
- Commit을 실행하지 않으면 쿼리가 실행되어도 테이블에 반영되지 않는다.
- Rollback을 실행하면 이전 상태로 돌아간다.
- 프로그래밍 상에 직접 명령을 할 수 없고 JPA에서 EntityManager 객체에서 Transaction 객체를 얻어 commit()을 호출해 반영 가능
JPA .commit() 동작 원리
- JPA API를 통해 EntityTransaction 객체를 얻고 commit() 메서드를 호출했을 때 동작
- TransactionImpl
- EntityTransaction 인터페이스의 구현체인 TransactionImpl에서 commit 호출
- 그 안에서 TransactionDriverControlImpl와 같은 로컬 트랜잭션 드라이버 구현 객체를 얻어서 commit 다시 호출
- JdbcResourceLocalTransactionCoordinatorImpl > TransactionDriverControlImpl
- JdbcResourceTransaction의 구현 객체인 AbstractLogicalConnectionImplementor의 commit()을 호출
- AbstractLogicalConnectionImplementor
- 물리적인 JDBC connection을 얻고 객체의 commit()을 호출
- Hibernate ORM 영역에서 처리되었다.
- JdbcConnection
- JDBC API 구현체인 H2 영역
- commit 명령을 준비하고 실행하는 메서드를 호출
- 메서드 호출은 Command 클래스에서 진행
- Command
- commitIfNonTransactional() 메서드를 호출
- 메서드 내부에서 autoCommit 여부를 체크해서 데이터 베이스 세션의 commit 수행
- 수행 과정에서 error가 발생하면 rollback 수행
- commitIfNonTransactional() 메서드를 호출
- SessionLocal
- 마지막으로 SessionLocal 클래스에서 트랜잭션 commit 수행
Spring Framwork Transaction 처리
선언형 트랜젝션 방식
Spring Boot에서의 트랜잭션 설정
- Spring boot를 사용하지 않는다면 configuration을 작성해야 한다.
- Datasource가 데이터베이스 커넥션 정보를 포함하고 있다.
- Spring에서 트랜잭션은 PlatformTransactionManager에 의해 관리
- PlatformTransactionManager 인터페이스를 구현한 JpaTransactionManager를 사용한다.
애너테이션 적용 방식
- @Transactional이라는 애너테이션을 필요한 영역에 추가한다.
- Service 클래스에 추가를 하면 Repository의 기능을 이요하는 모든 메서드에 트랜잭션 작용
- 트랜잭션 적용 확인을 위해 JPA 로그 레벨을 설정(application.yml)
- member를 추가했을 때 로그를 확인해보면
- 새로운 트랜잭션 생성
- 트랜잭션에서 commit 발생
- 트랜잭션 종료
- EntityManager 종료
- member를 생성하는 로직에 RuntimeException을 의도적으로 발생시키면
- 새로운 트랜잭션 생성
- Rolling back JPA transaction on EntityManager
- 위와 같이 rollback이 생성되는 것 확인 가능
- member를 추가했을 때 로그를 확인해보면
- logging: level: org: springframework: orm: jpa: DEBUG
- checked Exception은 rollback이 적용이 되지 않는다.
- @Transactional(rollbackFor={…})
- 위와 같이 직접 예외를 지정하거나 unchecked exception으로 감싸서 rollback 작동
- @Transactional(rollbackFor={…})
- 메서드 레벨에 @Transactional 적용
- @Transactional(readOnly=true)를 적용하면
- 해당 메서드는 commit을 실행하기는 하지만 영속성 콘텍스트를 flush하지 않는다.
- 읽기 전용은 변경 감지를 위한 스냅샷도 진행하지 않는다.
- 불필요한 동작 최소화
- PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
- 트랜잭션 생성할 대 위와 같이 readOnly로 설정된다.
- @Transactional(readOnly=true)를 적용하면
여러 작업이 하나의 트랜잭션으로 묶이는 경우
- 커피 주문을 하여 고객 스탬프가 적립되는 상황
- orderService에서 트랜잭션이 실행되고 memberService에서 update 하면서 트랜잭션 실행
- 독립적으로 실행될 경우 ACID에서 원자성이 실행이 안된다.
- createOrder() 메서드에 스탬프를 업데이트하는 update 메서드를 실행하여 메서드들이 하나의 트랜잭션 경계 내에 있도록 하여 묶는다.
- 실행 예시
- orderService에 @Transactional을 추가하고 createOrder 메서드 안에 반환 직전에 RuntimeException을 의도적으로 발생
- memberService에서 updateMember에 @Transactional(propagation=Propagation.REQUIRED)를 추가한다.
- 이것은 현재 진행 중인 트랜잭션이 존재하면 그 트랜잭션을 사용하고 존재하지 않으면 새로 생성하여 진행한다.
- 실행 결과
- rollback 로그 확인 가능
- H2 웹 콘솔에서 order 정보와 stamp정보가 반영되지 않은 것 확인 가능
트랜잭션 전파
- 트랜잭션의 경계에서 진행 중인 트랜잭션이 존재 유무에 따라 작동 방식 설정
- Propagation.REQUIRED
- propagation의 디폴트 값으로 진행 중인 트랜잭션 없으면 생성, 있으면 해당 트랜잭션 참여
- Propagation.REQUIRES_NEW
- 진행 중인 것과 상관없이 새로운 트랜잭션 시작, 기존 진행 중인 트랜젝션은 새로 시작된 트랜잭션 종료 시까지 일시 중지
- Propagation.MANDATORY
- 진행 중인 트랜잭션이 없으면 예외를 발생
- Propagation.NOT_SUPPORTED
- 트랜잭션이 필요 없음을 의미, 진행 중인 트랜잭션이 있다면 일시 중지하고 메서드 수행하고 종료되면 다시 재개
- Propagation.NEVER
- 진행 중인 트랜잭션이 존재할 경우 예외 발생
- Propagation.REQUIRED
트랜잭션 격리 레벨(Isolation Level)
- ACID의 독립성을 지키기 위해 제공하는 애트리뷰트
- Isolation.DEFAULT
- 데이터베이스 제공 기본값
- Isolation.READ_UNCOMMITTED
- 다른 트랜잭션에서 커밋하지 않은 데이터를 읽는 것 허용
- Isolation.READ_COMMITTED
- 다른 트랜잭션에서 커밋된 데이터를 읽는 것 허용
- Isolation.REPEATABLE_READ
- 트랜잭션 내 한 번 조회한 데이터를 반복 조회해도 같은 데이터 조회
- Isolation.SERIALIZABLE
- 동일한 데이터 동시에 두 개 이상 트랜잭션 수행 불가
- Isolation.DEFAULT
AOP 적용 방식
- AOP 트랜잭션 적용을 위한 Configuration 클래스 정의
- @Configuration을 추가하여 클래스 정의
- TransactionManager DI
- TransactionManager 객체를 DI로 받는다.
- @Bean을 추가한 TransactionInterceptor를 반환하는 txAdvice()를 생성
- NameMatchTransactionAttributeSource 객체로 txAttributeSource를 생성
- RuleBasedTransactionAttribute 객체로 txAttribute와 txFindAttribute 생성
- 각각 setPropagationBehavior로 TransactionDefinition.PROPAGATION_REQUIRED 설정
- txFindAttribute는 setReadOnly(true) 설정
- Map <String, TransactionAttribute>로 txMethods 생성
- .put(”find*”, txFindAttribute) 와 .put(”*”, txAttribute)로 추가
- txAttributeSource에 .setNameMap(txMethods)로 설정
- 마지막으로 TransactionInterceptor(transactionManager, txAttributeSource)로 생성해서 반환
- @Bean을 추가한 Advisor를 반환하는 txAdvisor() 생성
- AspectJExpressionPointcut 객체 pointcut을 생성
- pointcut에 .setExpression(…) 으로 매개변수에 적용할 경로 작성
- 마지막으로 DefDefaultPointcutAdvisor(pointcut, txAdvice())를 return 한다.
'Backend boot camp > Session3' 카테고리의 다른 글
[Spring MVC] API문서화 (0) | 2022.11.11 |
---|---|
[Spring MVC] Testing (0) | 2022.11.11 |
[Spring MVC] JPA 데이터 액세스 계층 (0) | 2022.11.07 |
[Spring MVC] JDBC DB Access Layer (0) | 2022.10.29 |
[Spring MVC] Exception Handle (0) | 2022.10.29 |