데이터베이스 트랜잭션을 관리하기 위해 사용된다.
스프링부트 JPA 를 이용하여 프로젝트를 진행할 경우 데이터를 읽고 쓰고 저장하는 일련의 모든 데이터 연산에 트랜젝션 단위가 이용된다.
트랜잭션 내 연산은 모두 독립적으로 이루어지며, 그 과정 중간에 다른 연산을 수행할 수 없고, 하나의 연산이라도 오류가 생겼다면 해당 트랙잭션은 취소되고 모두 원래대로 돌아가야 한다. 반드시 모든 연산이 성공해야만 트랜잭션이 성공됐다고 본다.
트랜잭션은 여러 데이터베이스 작업을 하나의 논리적인 단위로 묶어서 원자성, 일관성, 독립성, 지속성 ACID
속성을 보장한다.
🐾 ACID는 데이터베이스 트랜잭션이 안전하게 수행된다는 거승ㄹ 보장하기 위한 성질을 가리키는 약어이다.
트랜잭션과 관련된 작업들이 부분적으로 실행되다가 중단되지 않는 것을 보장하는 능력이다.
예를 들어, 자금 이체는 성공할 수도 실패할 수도 있지만 보내는 ㅉㄱ에서 돈을 빼 오는 작업만 성공하고 받는 쪽에 돈을 넣는 작업을 실패해서는 안된다.
이와 같이 중간 단계까지 실행되고 실패하는 일이 없도록 한다.
트랜잭션이 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 유지하는 것을 의미한다.
무결성 제약이 모든 계좌는 잔고가 있어야 한다면 이를 위반하는 트랜잭션은 중단된다.
트랜잭션을 수행 시 다른 트랜잭션의 연산 작업이 끼어들지 못하도록 보장하는 것을 의미한다.
트랜잭션 밖에 있는 어떤 연산도 중간 단계의 데이터를 볼 수 없음을 의미한다.
은행 관리자는 이체 작업을 하는 도중에 쿼리를 실행하더라도 특정 계좌간 이체하는 양 쪽을 볼 수 없다.
공식적으로 고립성은 트랜잭션 실행내역은 연속적이어야 함을 의미한다.
성공적으로 수행된 트랜잭션은 영원히 반영되어야 함을 의미한다.
시스템 문제, DB 일관성 체크 등을 하더라도 유지되어야 함을 의미한다.
전형적으로 모든 트랜잭션은 로그로 남고 시스템 장애 발생 전 상태로 되돌릴 수 있다.
트랜잭션은 로그에 모든 것이 저장된 후에만 commit 상태로 간주될 수 있다.
🐾 은행 애플리케이션에서 돈을 송금하는 기능을 구현한다고 가정한다.
🐾 보내는 사람의 계쫘에서 돈을 출금하고, 받는 사람의 계좌에 돈을 입금하는 두 개의 데이터베이스 작업을 포함한다.
@Transactional
public void transferMoney(int senderAccountId, int receiverAccountId, double amount) {
// 보내는 사람의 계좌에서 돈 출금
Account senderAccount = accountRepository.findById(senderAccountId);
senderAccount.withdraw(amount);
accountRepository.save(senderAccount);
// 받는 사람의 계좌에 돈 입금
Account receiverAccount = accountRepository.findById(receiverAccountId);
receiverAccount.deposit(amount);
accountRepository.save(receiverAccount);
}
위의 코드에서 Transactional 어노테이션은 transferMoney 메서드에 적용되어 있다.
어느 한 작업이 실패하면 다른 작업도 롤백되어 이전 상태로 복구된다.
Transactional 어노테이션은 다른 상황에서도 유용하게 사용될 수 있다.
정리 하자면, Transactional 어노테이션은 데이터베이스 작업을 트랜잭션 단위로 묶어서 안전하고 일관된 상태를 유지하도록 도와주는 기능을 제공한다.
트랜잭션 시작
메서드 실행
예외 발생 여부 확인
예외 발생 시 롤백, 예외 미발생 시 커밋
트랜잭션 내에서 어떤 작업이 실패했을 때, 해단 작업 이전의 상태로 트랜잭션을 되돌리는 것을 의미한다.
트랜잭션 관리 시스템이 제공하는 기능으로, 예외가 발생했을 때 자동으로 수행되기도 하며, 개발자가 명시적으로 롤백을 호출할 수도 있다.
예를 들어, 트랜잭션 내에서 3개의 데이터베이스 작업이 실행되고, 세 번째 작업에서 예외가 발생한다면, 롤백이 수행되어 첫 번째와 두 번째 작업이 최소되고 이전 상태로 복구된다.