본문 바로가기
Java , Spring/Spring

[Spring] 선언적 트랜잭션 @Transactional

by 방배킹 2024. 1. 22.

@Transactional에 대해 알아보기 전에 트랜잭션이 무엇인지 먼저 알아보자

트랜잭션이란?

데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위이다.

 

트랜잭션의 특징 (ACID)

  • 원자성 (Atomicity)
    • 트랜잭션 내에서 실행한 작업들은 하나의 작업으로 처리해야한다. (모두 성공하거나 모두 실패하거나)
  • 일관성 (Consistency)
    • 모든 트랜잭션은 일관성있는 데이터베이스 상태를 유지해야한다. (데이터베이스의 규칙을 지켜야한다)
  • 지속성 (Durability)
    • 트랜잭션이 성공적으로 끝나면 그 결과가 항상 기록되어야 한다.
  • 격리성 (Isolation)
    • 동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않도록 격리한다.

트랜잭션은 원자성, 일관성, 지속성을 보장하지만 격리성을 완벽히 보장하기 위해서는 트랜잭션을 순서대로 진행해야한다.

 

그러면 동시처리 성능이 매우 나빠진다.

 

ANSI 표준은 트랜잭션 격리 수준을 4단계로 나누어 정의했다.

 

트랜잭션 격리 수준 - Isolation level

  • READ UNCOMMITED(커밋되지 않은 읽기)
  • READ COMMITTED(커밋된 읽기)
  • REPEATABLE READ(반복 가능한 읽기)
  • SERIALIZABLE(직렬화 가능)

 

예시 ) A의 계좌에서 B의 계좌로 10000원을 이체하는 경우

 

1. A의 계좌에서 10000원 감소

2. B의 계좌에서 10000원 증가

 

위와 같이 2개의 작업이 일어나야 한다. 그런데 1,2 중 하나만 성공하고 하나는 실패한다면 문제가 발생한다.

따라서 모든 작업이 성공했을 경우에 commit을 하고 중간에 에러가 발생하면 rollback을 해야한다.

 

자동커밋과 수동커밋

  • 자동 커밋: 쿼리 실행이후 자동으로 커밋을 호출한다.
    • 직접 커밋, 롤백을 호출하지 않아도 되서 편리하지만 쿼리 하나하나 바로 자동으로 커밋되기 때문에 트랜잭션 기능을 제대로 사용할 수 없다. 
set autocommit true; //자동 커밋 모드 설정
insert into member(member_id, money) values ('data1',10000); //자동 커밋
insert into member(member_id, money) values ('data2',10000); //자동 커밋
  • 수동 커밋: 쿼리 실행이후 직접 커밋과 롤백등을 호출해야한다.
    • 자동커밋이 기본값이므로 set autocommit false; 를 통해 수동커밋을 설정한다 (트랜잭션 시작이라고 부른다)
    • 이후 commit을 호출해줘야 한다.
set autocommit false; //수동 커밋 모드 설정
insert into member(member_id, money) values ('data3',10000);
insert into member(member_id, money) values ('data4',10000);
commit; //수동 커밋

 

선언적 트랜잭션 (@Transactional)

@Transactional은 스프링 프레임워크에서 제공하는 어노테이션으로, 메서드 단위로 트랜잭션을 관리할 수 있도록 도와준다. (Spring에서는 @Transactional을 AOP를 를 사용해서 구현한다)

즉, JDBC 환경에서 수동 커밋과 비슷하지만 수동커밋의 경우 try catch를 통해 exception을 잡아서 rollback하는 코드를 작성해야하지만 @Transactional은 예외가 발생하면 자동으로 rollback을 해준다.

 

@Service
public class MyService {

    @Autowired
    private MyRepository myRepository;

    @Transactional
    public void performTransactionalOperation() {
        // 여러 데이터베이스 작업 수행
        myRepository.saveData1();
        myRepository.saveData2();
        // ...
    }
}

 

Checked Exception, Unchecked Exception

@Transactional은 UncheckedException이 발생했을때만 rollback을 한다.

CheckedException이 발생했을때는 rollback을 하지 않는다.

 

UncheckedException 이랑 CheckedException은 뭔데?

https://bangbaeking.tistory.com/103

 

[Java] UncheckedException과 CheckedException (Error와 Exception)

Error와 Exception 먼저 Error와 Exception의 차이에 대해 알아보자 Error(오류)는 시스템이 종료되어야 할 수준의 상황과 같이 수습할 수 없는 심각한 문제를 의미한다. 개발자가 미리 예측하여 방지할 수

bangbaeking.tistory.com

 

Checked Exception

  • Complie Exception이라고도 하며 Exception을 바로 상속받는다.
  • 컴파일 시점에 예외를 catch 하는지 정적으로 확인한다. 컴파일 시점에 예외에 대해 처리(try/catch) 하지 않는다면 컴파일 에러가 발생한다.
  • 트랜잭션 Rollback이 안된다.

Unchecked Exception

  • Unchecked Exception은 RuntimeException을 상속받는다.
  • 컴파일 시점에 예외를 catch 하는지 확인하지 않는다. 따라서 컴파일 시점에 예외가 발생하는지 판단할 수 없다.
  • 트랜잭션 Rollback이 된다.

 

Checked Exception은 반드시 처리를 해야한다.

Checked Exception이 발생하면 롤백을 하지 않으므로 로직을 통해 예외처리를해서 해결을 해야한다.

 

 

 

여러가지 경우의 @Transactional

 

1. RuntimeException (Unchecked Exception)이 발생하면 rollback 처리된다.

@Transactional
public void performTransactionalOperation() {

    myRepository.saveData1();
        
    //RuntimeException
    throw new RuntimeException("new RuntimeException");
    
    //rollback 
    myRepository.saveData2(); // saveData2() 실행 X
}

 

2. CheckedException은 발생해도 rollback 없이 정상 반영된다.

@Transactional
public void performTransactionalOperation() {

    myRepository.saveData1();
        
    //Checked Exception
    throw new SQLException("new SQLException");
    
    //rollback X
    myRepository.saveData2(); // saveData2() 실행
}

 

3. rollbackfor을 사용하면 CheckedException이 발생해도 rollback을 할수 있다.

@Transactional(rollbackFor = SQLException.class)
public void performTransactionalOperation() {

    myRepository.saveData1();
        
    //Checked Exception
    throw new SQLException("new SQLException");
    
    //rollback O
    myRepository.saveData2(); // saveData2() 실행 X
}

 

 

 

Reference

 

 

댓글