
트랜잭션이란
Spring Boot에서 트랜잭션은 데이터베이스 작업을 하나의 작업 단위로 묶어 처리하는 것을 의미한다. 트랜잭션을 사용하면 일련의 데이터베이스 작입이 모두 성공적으로 완료되거나, 어느 하나라도 실패하면 모두 취소되는 것을 보장할 수 있다. 이는 데이터 무결성과 일관성을 유지하는데 매우 중요하다.
Spring Boot에서 트랜잭션을 사용하려면 @transactional 어노테이션을 활용한다. @Transactional 어노테이션은 클래스나 메소드에 적용하여 해당 범위 내의 데이터베이스 작업이 트랜잭션으로 처리되도록 한다.
트랜잭션 특징
- 원자성 : 트랜잭션의 모든 연산이 모두 반영되거나, 모두 반영되지 않아야 한다.
- 일관성 : 트랜잭션이 성공적으로 완료되면, DB는 일관성 있는 상태를 유지한다.
- 격리성 : 여러 트랜잭션이 동시에 수행되더라도 각 트랜잭션은 독립적으로 실행되어야 한다.
- 지속성 : 트랜잭션이 성공적으로 커밋되면 그 결과는 영구적으로 DB에 저장된다.
트랜잭션 설정
기본 트랜잭션
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
/**
* 기본적인 트랜잭션 설정. 이 메서드가 호출되면 트랜잭션이 시작되고,
* 메서드가 정상적으로 완료되면 트랜잭션이 커밋.
* 예외가 발생하면 트랜잭션이 롤백.
*/
@Transactional
public void createUser(User user) {
userRepository.save(user);
}
}
트랜잭션 전파
import org.springframework.transaction.annotation.Propagation;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
/**
* 트랜잭션 전파 속성을 REQUIRED로 설정.
* 현재 트랜잭션이 존재하면 해당 트랜잭션을 사용하고,
* 없으면 새로운 트랜잭션을 생성 (기본값).
*/
@Transactional(propagation = Propagation.REQUIRED)
public void updateUser(User user) {
userRepository.save(user);
}
/**
* 트랜잭션 전파 속성을 REQUIRES_NEW로 설정.
* 항상 새로운 트랜잭션을 생성.
* 기존 트랜잭션이 있으면 일시 중지.
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createNewUser(User user) {
userRepository.save(user);
}
}
트랜잭션 전파 옵션
- REQUIRED : 현재 트랜잭션이 존재하면 사용, 없으면 새 트랜잭션을 생성 (기본값)
- REQUIRES_NEW : 항상 새 트랜잭션을 생성. 기존 트랜잭션이 있으면 일시 중지
- NESTED : 중첩 트랜잭션을 생성. 기존 트랜잭션이 없으면 새 트랜잭션을 생성
트랜잭션 격리 수준
import org.springframework.transaction.annotation.Isolation;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
/**
* 트랜잭션 격리 수준을 READ_COMMITTED로 설정.
* 다른 트랜잭션이 커밋한 변경 사항만 읽을 수 있다.
*/
@Transactional(isolation = Isolation.READ_COMMITTED)
public void processUser(User user) {
userRepository.save(user);
}
/**
* 트랜잭션 격리 수준을 SERIALIZABLE로 설정.
* 가장 높은 격리 수준으로, 완전한 트랜잭션 직렬화를 보장.
*/
@Transactional(isolation = Isolation.SERIALIZABLE)
public void processCriticalUser(User user) {
userRepository.save(user);
}
}
트랜잭션 격리 수준 옵션
- READ_UNCOMMITTED : 다른 트랜잭션이 커밋되지 않은 변경 사항을 읽을 수 있다.
- READ_COMITTED : 다른 트랜잭션이 커밋한 변경 사항만 읽을 수 있다.
- REPEATABLE_READ : 트랜잭션이 시작된 시점부터 커밋된 변경 사항만 읽을 수 있다.
- SERIALIZABLE : 가장 높은 격리 수준으로, 완전한 트랜잭션 직렬화를 보장한다.
트랜잭션 시간 제한 및 읽기 전용 설정
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
/**
* 트랜잭션 시간 제한을 5초로 설정.
* 5초 안에 트랜잭션이 완료되지 않으면 롤백.
*/
@Transactional(timeout = 5)
public void performTimeSensitiveOperation(User user) {
userRepository.save(user);
// 추가적인 데이터베이스 작업 수행
}
/**
* 트랜잭션을 읽기 전용으로 설정.
* 데이터베이스에 변경을 하지 않을 시 사용하여 성능을 최적화.
*/
@Transactional(readOnly = true)
public User getUser(Long userId) {
return userRepository.findById(userId).orElse(null);
}
}
트랜잭션 롤백 조건 설정
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
/**
* 특정 예외가 발생하면 롤백되도록 설정.
* IllegalArgumentException이 발생하면 트랜잭션을 롤백.
*/
@Transactional(rollbackFor = IllegalArgumentException.class)
public void createUserWithRollback(User user) {
userRepository.save(user);
// 강제로 예외를 발생시켜 롤백 테스트
if (user.getName() == null) {
throw new IllegalArgumentException("User name cannot be null");
}
}
/**
* 특정 예외를 무시하고 커밋되도록 설정.
* IllegalArgumentException이 발생해도 롤백되지 않습니다.
*/
@Transactional(notRollbackFor = IllegalArgumentException.class)
public void createUserWithoutRollback(User user) {
userRepository.save(user);
// 강제로 예외를 발생시키지만 롤백되지 않음
if (user.getName() == null) {
throw new IllegalArgumentException("User name cannot be null");
}
}
}
커스텀 트랜잭션 관리
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PlatformTransactionManager transactionManager;
/**
* Programmatic 트랜잭션 관리. 직접 트랜잭션을 제어하는 방법.
*/
public void programmaticTransactionManagement(User user) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def);
try {
userRepository.save(user);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
'Spring Boot' 카테고리의 다른 글
[Spring Boot] 암호화/복호화(AES, RSA, BCrypt) (2) | 2024.07.24 |
---|---|
[Spring Boot] 어노테이션(Annotation) (0) | 2024.07.24 |
[Spring Boot] 필터(Filter) (2) | 2024.07.24 |
[Spring Boot] Controller, Service, Mapper, DTO (0) | 2024.07.18 |
[Spring Boot] Batch 기본 개념 (0) | 2024.07.18 |