@Transactional은 Spring Framework에서 제공하는 트랜잭션 관리를 위한 애너테이션이다.
데이터베이스와의 작업이 완벽하게 수행 되도록 보장하기 위해 사용되며, 모든 작업이 성공하거나 아니면 모두 롤백되도록 한다.
예를 들어, 데이터베이스에 여러 레코드를 추가하는 작업 중 하나라도 실패한다면, 전체 작업을 롤백하여 이전 상태로 복구한다. 이렇게 하면 데이터 일관성을 유지할 수 있다.
아래는 은행 송금 예제이다. 사용자가 A 계좌에서 B 계좌로 100원을 송금할 때, 두 가지 작업이 필요하다.
1. A 계좌에서 100원을 출금한다.
2. B 계좌에서 100원을 입금한다.
1. Service 코드(송금 로직)
@Service
@RequiredArgsConstructor
public class BankService {
private final AccountRepository accountRepository;
// 이 메서드에 @Transactional을 사용하여 송금 도중 문제가 생기면 전체 작업을 롤백합니다.
@Transactional
public void transferMoney(Long fromAccountId, Long toAccountId, int amount) {
// A 계좌에서 출금
Account fromAccount = accountRepository.findById(fromAccountId)
.orElseThrow(() -> new IllegalArgumentException("출금 계좌를 찾을 수 없습니다."));
fromAccount.withdraw(amount); // 금액 차감
// B 계좌에 입금
Account toAccount = accountRepository.findById(toAccountId)
.orElseThrow(() -> new IllegalArgumentException("입금 계좌를 찾을 수 없습니다."));
toAccount.deposit(amount); // 금액 추가
// 변경된 계좌 정보를 저장
accountRepository.save(fromAccount);
accountRepository.save(toAccount);
}
}
2. Account 엔티티
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String owner;
private int balance;
// 출금 메서드
public void withdraw(int amount) {
if (this.balance < amount) {
throw new IllegalArgumentException("잔액이 부족합니다.");
}
this.balance -= amount;
}
// 입금 메서드
public void deposit(int amount) {
this.balance += amount;
}
}
3. Repository 코드
public interface AccountRepository extends JpaRepository<Account, Long> {
}
@Transactional(rollbackFor = Exception.class) // 모든 예외 발생 시 롤백
public void transferMoney(Long fromAccountId, Long toAccountId, int amount) {
// 송금 로직
}
읽기 전용 트랜잭션에서는 DB에 대한 변경 작업이 허용되지 않는다.
Hibernate는 이 설정을 통해 성능을 최적화한다.
주로 조회 전용 메서드에 사용한다.
@Transactional(readOnly = true)
public List<Account> getAllAccounts() {
return accountRepository.findAll();
}