transaction (2)

 

 

 

 

Spring에서의 트랜잭션

 

 

트랜잭션은 크게 로컬 트랜잭션분산 트랜잭션으로 구분된다.

 

Spring에서 사용되는 트랜잭션 방식은 선언형 트랜잭션 방식프로그래밍 코드 베이스 트랜잭션 방식이 있다.

 

트랜잭션은 애플리케이션 내에서 핵심 로직이 아닌 부가 기능이기 때문에 AOP 적용 대상이다.

➡️ 프로그래밍 코드 베이스 방식으로 트랜잭션을 적용하는 방식은 적절하지 않다.

 

 

 

Spring에서 선언형 방식의 트랜잭션 적용

 

Spring에서 선언형 방식으로 트랜잭션을 적용하는 방식은 크게 두가지가 있다.

1. 작성한 비즈니스 로직에 애너테이션을 추가

2. AOP 방식을 이용해서 비즈니스 로직에서 아예 트랜잭션 적용 코드를 감추는 방식

 

 

 

Spring Boot에서의 트랜잭션 설정

 

트랜잭션은 데이터베이스 인터랙션과 관련이 있기 때문에 커넥션 정보를 포함하고 있는 Datasource 가 기본적으로 필요하다.

Spring에서 트랜잭션은 기본적으로 PlatformTransactionManager 인터페이스에 의해 관리된다.

PlatformTransactionManager를 구현해서 유연하게 트랜잭션을 적용할 수 있다.

JPA 데이터 액세스 기술은 PlatformTransactionManager의 구현 클래스인 JpaTransactionManager를 사용한다.

 

 

 

 

애너테이션 방식의 트랜잭션 적용

 

@Transactional

Spring에서 가장 간단하게 트랜잭션을 적용할 수 있는 방법이다.

클래스에 추가하면 모든 메서드에 적용되서 어떤 메서드든 호출하면 하나의 새로운 트랜젝션이 생성된다.

rollback 없이(에러나 예외 없이) 메서드 호출이 끝나면 트랜젝션에서 commit이 일어나고 트랜젝션이 종료된다.

중간에 예외가 발생할 경우 rollback 되서 데이터베이스가 변경됐던 부분이 반영되지 않는다.

 

 

▶️ JPA 로그 레벨 설정

 

application.yml(resources 디렉토리에 있다.)에 해당 코드 추가

 

logging:
  level:
    org:
      springframework:
        orm:
          jpa: DEBUG

 

로그 레벨을 ‘DEBUG’ 레벨로 설정 ➡️ JPA 내부에서 ‘DEBUG’ 로그 레벨을 지정한 부분의 로그를 확인할 수 있다.

 

 

✔️ 참고) 로그 레벨

 TRACE  <  DEBUG  <  INFO  <  WARN  <  ERROR

  • ERROR : 요청을 처리하는 중 오류가 발생한 경우 표시한다.
  • WARN : 처리 가능한 문제, 향후 시스템 에러의 원인이 될 수 있는 경고성 메시지를 표시한다.
  • INFO : 상태변경과 같은 정보성 로그를 표시한다.
  • DEBUG : 프로그램을 디버깅하기 위한 정보를 표시한다.
  • TRACE : Debug보다 훨씬 상세한 정보를 나타낸다.

 

 

 

체크 예외(checked exception)는 rollback이 잘 될까?

 

체크 예외 예시 : Exception, SQLException, DataFormatException,...

체크 예외(checked exception)는 @Transactional 애너테이션만 추가해서는 rollback 되지 않는다.

체크 예외는 캐치(catch)한 후에 어떻게 처리할 지 결정해야 한다.

만일 예외 전략을 짤 필요가 없다면

@Transactional(rollbackFor = {SQLException.class, DataFormatException.class})

같이 체크 예외를 직접 지정해주거나 언체크 예외(unchecked exception)로 감싸서 rollback을 수행한다.

 

 

 

▶️ 메서드 레벨에 @Transactional 적용

 

메서드 레벨에도 @Transactional 에너테이션을 적용할 수 있다.

메서드 위에 작성하고 작성한 메서드만 트랜젝션을 적용한다.

 

Attribute

  • readOnly
    • 값으로 true 를 주면 읽기 전용 메서드가 된다.
    • commit 절차는 진행한다.
    • JPA 내부적으로 영속성 컨텍스트를 flush 하지 않는다. (변경 감지를 위한 스냅샷 생성 x)
    • 내부적으로 불필요한 추가 동작을 줄여서 성능을 최적화 할 수 있다.

 

 

※ @Transactional (readOnly = true)

➡️ readOnly를 찾을 수 없다고 뜨는 경우

Transactional import를 javax.transaction 이 아닌

org.springframework.transaction.annotation.Transactional 에서 해야 한다.

두 개의 라이브러리에 tranasactional 애너테이션이 존재하므로 import 할 때 주의한다.

 

 

▶️ 클래스 레벨과 메서드 레벨의 트랜잭션 적용 순서

 

✔️ 클래스 레벨에만 @Transactional이 적용된 경우

➡️ 클래스 레벨의 @Transactional이 메서드에 일괄 적용된다.

 

✔️ 클래스 레벨과 메서드 레벨에 함께 적용된 경우

메서드 레벨의 @Transactional이 적용된다.

만약 메서드 레벨의 @Transactional이 적용되지 않았다면 클래스 레벨의 @Transactional 이 적용된다.

 

 

 

➤ 트랜잭션 전파(Transaction Propagation)

 

@Transactional(propagation = Propagation.REQUIRED)

 

트랜잭션 Propagation?

트랜잭션의 경계에서 진행 중인 트랜잭션이 존재할 때 또는 존재하지 않을 때, 어떻게 동작할 것인지 결정하는 방식을 의미한다.

 

propagation attribute를 통해 설정할 수 있다.

 

▶️ propagation의 대표적 유형

1. Propagation.REQUIRED

propagation 중 가장 일반적으로 사용된다.

현재 진행 중인 트랜잭션이 존재하면 해당 트랜잭션을 사용하고, 존재하지 않으면 새 트랜잭션을 생성하도록 해준다.

 

2. Propagation.REQUIRES_NEW

이미 진행 중인 트랜잭션과 무관하게 새로운 트랜잭션이 시작된다.

기존에 진행중이던 트랜잭션은 새로 시작된 트랜잭션이 종료할 때까지 중지된다.

 

3. Propagation.MANDATORY

진행 중인 트랜잭션이 없으면 예외를 발생시킨다.

 

4. Propagation.NOT_SUPPORTED

트랜잭션을 필요로 하지 않다는 것을 의미한다.

진행 중인 트랜잭션이 있으면 메서드 실행이 종료될 때까지 진행중인 트랜잭션은 중지하며, 메서드 실행이 종료되면 트랜잭션을 계속 진행한다.

 

5. Propagation.NEVER

트랜잭션을 필요로 하지 않다는 것을 의미한다.

진행중인 트랜잭션이 존재할 경우에 예외를 발생시킨다.

 

 

 

➤ 트랜잭션 격리 레벨(Isolation Level)

 

트랜잭션은 다른 트랜잭션에 영향을 주지 않고, 독립적으로 실행되어야 하는 격리성이 보장되어야 한다.(ACID 원칙 중 Isolation)

트랜잭션 Isolation은 동시 트랜잭션에 의해 적용된 변경 사항이 서로에게 어떻게 보이는지 설정한다.

각각의 isolation 레벨은 트랜잭션이 동시에 발생할 때 생길 수 있는 아래 문제들을 예방한다.

  1. Dirty read : 동시 트랜잭션에서 커밋되지 않은 변화를 읽을 수 있다.
  2. Nonrepeatable read : 동시 트랜잭션이 동일한 행을 업데이트하고 커밋하는 경우 행을 다시 읽을 때 다른 값을 얻는다.
  3. Phantom read : 다른 트랜잭션이 범위의 일부 행을 추가하거나 제거하고 커밋하는 경우 쿼리를 다시 실행한 후 다른 행을 가져온다.

Spring 에서 @Transactional 의 attribute로 isolation을 통해 트랜잭션 간의 격리성을 제공한다.

 

1. Isolation.DEFAULT

데이터베이스에서 제공하는 기본 값

 

2. Isolation.READ_UNCOMMITTED

다른 트랜잭션에서 커밋하지 않은 데이터를 읽는 것을 허용한다.

 

3. Isolation.READ_COMMITTED

다른 트랜잭션에 의해 커밋된 데이터를 읽는 것을 허용한다.

 

4. Isolation.REPEATABLE_READ

트랜잭션 내에서 한 번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회되도록 한다.

 

5. Isolation.SERIALIZABLE

동일한 데이터에 대해서 동시에 두 개 이상의 트랜잭션이 수행되지 못하도록 한다.

 

 

https://www.baeldung.com/spring-transactional-propagation-isolation

 

Transaction Propagation and Isolation in Spring @Transactional | Baeldung

Learn about the isolation and propagation settings in Spring's @Transactional

www.baeldung.com

 

 

 

 

 

AOP 방식의 트랜잭션 적용

 

 

1. AOP 방식으로 트랜잭션 적용을 위한 Configuration 클래스 정의

@Configuration 애너테이션 추가

 

2. TransactionManager DI

애플리케이션에 트랜잭션 적용하기 위해서는 TransactionManager라는 객체가 필요하다.

DI(의존성 주입)를 통해 TransactionManager 객체를 받는다.

 

3. 트랜잭션 advice용 TransactionInterceptor 빈 등록

Spring에서 TransactionInterceptor를 이용해서 대상 클래스 / 인터페이스에 트랜잭션 경계를 설정하고 트랜잭션을 적용할 수 있다.

  • 트랜잭션 attribute 지정
  • 트랜잭션을 적용할 매서드에 트랜잭션 애트리뷰트 매핑
  • TransactionInterceptor 객체 생성

 

4. Advisor 빈 등록

  • 포인트 컷 지정
  • Advisor 객체 생성

 

 

 

 

✅ 추가 학습이 필요한 부분

 

✔️ 분산 트랜잭션

서로 다른 데이터 소스를 사용하는 한 개 이상의 데이터베이스를 하나의 트랜잭션으로 묶어서 처리해야 하는 것을 분산 트랜잭션이라고 한다.

분산 트랜잭션을 적용하면 서로 다른 리소스에 대한 작업을 수행하더라도 하나의 작업처럼 관리하기 때문에 원자성을 보장할 수 있다.

WAS가 제공하는 JTA(Java Transaction API) 서비스 이용

Atomikos는 Spring Boot에서 스타터로 지원하는 가장 인기있는 오픈 소스 트랜잭션 관리자이다.

 

 

 

 

 

 

감사합니다.

오개념에 대한 지적은 늘 환영입니다. 😄

 

 

 

 

 

트랜잭션(Transaction)이란?

 

 

트랜잭션 : 여러개의 작업들을 하나의 그룹으로 묶어서 처리하는 처리 단위

두 개 이상의 작업들이 그룹처럼 묶여서 처리될 수 있는데 하나라도 실패할 경우 애플리케이션의 신뢰성이 깨어지는 상황이 발생할 수 있다.

애플리케이션에서 신뢰성이 깨어지는 상황이 발생하면 트랜잭션이라고 부를 수 없다.

 

물리적으로는 여러 개의 작업이지만 논리적으로는 마치 하나의 작업으로 인식하는 경우

전부 성공 또는 전부 실패 (All or Nothing)로만 처리되어야 트랜잭션의 의미를 가진다.

 

 

 

➤ ACID 원칙

 

데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질을 가리키는 약어이다.

 

 

1. 원자성 (Atomicity)

트랜잭션에서 작업을 더 이상 쪼갤 수 없다는 것을 의미한다.

모두 성공하거나 모두 실패하거나(All or Nothing) 

 

2. 일관성 (Consistency)

트랜잭션이 에러없이 성공적으로 끝날 경우, 비즈니스 로직에서 의도하는대로 일관성있게 저장되거나 변경되는 것을 의미한다.

 

3. 격리성 (Isolation)

여러 개의 트랜잭션이 실행될 경우 각각 독립적으로 실행되어야 함을 의미한다.

 

4. 지속성 (Durability)

트랜잭션이 완료되면 그 결과는 지속되어야 한다는 의미이다.

 

 

 

➤ 트랜잭션 커밋(commit)과 롤백(rollback)

 

 

▶️ commit

  • 모든 작업을 최종적으로 데이터베이스에 반영하는 명령어
  • 커밋을 수행하면 변경된 내용이 DB에 영구적으로 저장된다.
  • 커밋 명령을 수행하면 하나의 트랜젝션 과정은 종료하게 된다.

 

 

▶️ rollback

  • 작업 중 하나라도 문제가 발생했을 경우 트랜잭션 내에서 수행된 작업들을 취소한다.
  • 트랜잭션 시작 이전의 상태로 되돌아간다.

 

 

 

➤ JPA의 Entity Transaction commit() 내부 들여다보기

 

EntityTransaction 객체 commit() 메서드 호출 시 어떤 일이 일어날까?

 

EntityTransaction 인터페이스의 구현 클래스인 TransactionImpl 클래스의 commit()을 호출한다.

-> TransactionDriverControlImpl (물리적인 트랜잭션을 제어하기 위한 로컬 트랜잭션 드라이버 구현 객체) 를 얻은 후 구현 메서드인 commit()을 다시 호출한다.

-> TransactionDriverControlImpl 에서 JDBC Connection의 액세스 방법을 제공하는 JdbcResourceTransaction의 구현객체인 AbstractLogicalConnectionImplementor 의 commit()을 호출한다. (여기까지가 Hibernate ORM에서의 영역)

-> 물리적인 JDBC Connection을 통해 데이터베이스와 인터랙션하기 위해서 JDBC API의 구현체인 JdbcConnection 영역으로 이동한다.

-> (여기부터 JDBC API의 구현체인 H2의 영역) 데이터베이스에 commit 명령을 준비(prepareCommand)한 후, 명령을 실행(executeUpdate)한다.

-> Command 클래스에서 메서드 실행된다.

데이터베이스에 commit 명령을 전달하기 위해 commitIfNonTransactional 메서드 호출한다.

commitIfNonTransactional() 메서드 내부에서 auto commit 여부를 체크한 후

데이터베이스 세션에 해당하는 Session 객체를 통해 commit 명령을 수행한다.

만약에 commitIfNonTransactional() 수행 과정에서 예외가 발생하면 rollback을 수행한다.

-> SessionLocal 클래스에서 트랜잭션에 대한 commit이 수행된다.

 

 

 

 

 

 

 

읽어주셔서 감사합니다.

오개념에 대한 지적은 늘 환영입니다.

 

 

 

1