Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- NIO
- 인프라
- 고전역학
- oracle
- JVM
- write by GPT-4
- 자바네트워크
- 리눅스
- 역학
- 데이터베이스
- 자바
- 자바암호
- 파이썬
- 유닉스
- GPT-4's answer
- 시스템
- write by chatGPT
- Database
- android
- 웹 크롤링
- python
- Java
- Spring boot
- kotlin
- chatGPT's answer
- 소프트웨어공학
- spring data jpa
- 코틀린
- jpa
- flet
Archives
- Today
- Total
Akashic Records
Spring Data JPA - @Transactional 본문
728x90
@Transactional
@Transactional은 Spring 프레임워크에서 제공하는 어노테이션으로, 트랜잭션 경계를 정의하고 관리하는 데 사용됩니다. 데이터베이스와의 상호작용을 포함한 메소드 실행 중 오류가 발생하면 자동으로 롤백(rollback)을 수행하여 데이터 일관성을 유지합니다. 이 어노테이션은 데이터 접근 로직을 단순화하고, 트랜잭션 관리에 필요한 반복적인 작업을 줄여주는 역할을 합니다.
아래에서는 @Transactional 어노테이션에 대해 자세히 설명하고, 주요 사용 방법 및 주의사항들을 다룹니다.
- @Transactional은 Spring에서 트랜잭션 경계를 정의하고 관리하는 데 사용하는 어노테이션으로, 데이터 무결성을 유지하는 데 중요한 역할을 합니다.
- 트랜잭션 전파 유형, 격리 수준, 타임아웃, 읽기 전용 등 다양한 속성을 통해 트랜잭션의 동작을 제어할 수 있습니다.
- Spring의 AOP 기반으로 동작하므로 메소드 간 호출, 예외 관리, 접근 제어 등에 주의가 필요합니다.
- 적절히 사용하면 트랜잭션 관리를 자동화하고 코드의 복잡성을 줄일 수 있지만, 설정이나 내부 동작 방식에 대한 깊은 이해가 필요합니다.
1. @Transactional의 주요 기능
@Transactional은 트랜잭션 관리를 위해 다음과 같은 기능을 제공합니다:
- 트랜잭션 경계 설정:
- 해당 메소드나 클래스에 트랜잭션을 적용하여 데이터 작업이 모두 성공적으로 수행되면 커밋(commit)되고, 오류가 발생하면 롤백(rollback)됩니다.
- 롤백 자동화:
- 메소드 실행 중 런타임 예외(RuntimeException) 또는 체크되지 않은 예외가 발생하면 트랜잭션을 자동으로 롤백합니다. 일반적으로 이러한 방식으로 데이터의 무결성을 유지합니다.
- 트랜잭션 전파(propagation):
- 기존의 트랜잭션이 있는지 여부와 관련하여 새로운 트랜잭션을 시작할지, 기존 트랜잭션을 사용할지 등을 설정할 수 있습니다. 이를 통해 다른 메소드 간의 트랜잭션 동작을 제어할 수 있습니다.
- 트랜잭션 격리 수준(isolation level):
- 동시성 제어를 위해 데이터베이스의 트랜잭션 격리 수준을 지정할 수 있습니다. 이를 통해 여러 사용자가 동일한 데이터를 동시에 액세스할 때 발생할 수 있는 문제를 방지합니다.
2. @Transactional 적용 범위
- 메소드 레벨:
- 특정 메소드에만 트랜잭션을 적용할 때 사용합니다. 이 경우 해당 메소드가 호출될 때 트랜잭션이 시작되고, 메소드가 종료될 때 커밋되거나 롤백됩니다.
@Transactional public void createUser(User user) { userRepository.save(user); }
- 클래스 레벨:
- 클래스 전체에 대해 트랜잭션을 적용합니다. 이 경우 해당 클래스의 모든 public 메소드가 트랜잭션 내에서 실행됩니다.
@Transactional public class UserService { public void updateUser(User user) { userRepository.save(user); } public void deleteUser(Long userId) { userRepository.deleteById(userId); } }
3. 주요 속성
@Transactional에는 트랜잭션 동작을 제어하기 위한 여러 속성들이 있습니다:
- propagation (전파 유형):
- 트랜잭션 전파 유형을 설정하여 메소드가 호출되는 상황에서 트랜잭션을 어떻게 처리할지 결정합니다.
- 주요 전파 유형:
- REQUIRED (기본값): 기존 트랜잭션이 있으면 그 트랜잭션에 참여하고, 없으면 새로운 트랜잭션을 시작합니다.
- REQUIRES_NEW: 항상 새로운 트랜잭션을 시작하며, 기존 트랜잭션이 있어도 일시 중지됩니다.
- SUPPORTS: 트랜잭션이 있는 경우 참여하고, 없으면 트랜잭션 없이 실행합니다.
- MANDATORY: 반드시 기존 트랜잭션이 있어야 합니다. 없으면 예외가 발생합니다.
- NEVER: 트랜잭션 없이 실행하며, 기존 트랜잭션이 있는 경우 예외를 발생시킵니다.
- NESTED: 기존 트랜잭션 내에서 중첩된 트랜잭션을 생성합니다.
@Transactional(propagation = Propagation.REQUIRES_NEW) public void performNewTransaction() { // 항상 새로운 트랜잭션 내에서 실행됩니다. }
- isolation (격리 수준):
- 트랜잭션의 격리 수준을 정의하여, 동시에 여러 트랜잭션이 실행될 때 데이터 무결성을 유지하는 방법을 설정합니다.
- 주요 격리 수준:
- READ_UNCOMMITTED: 다른 트랜잭션에서 아직 커밋되지 않은 데이터를 읽을 수 있습니다.
- READ_COMMITTED: 다른 트랜잭션에서 커밋된 데이터만 읽을 수 있습니다.
- REPEATABLE_READ: 동일한 트랜잭션 내에서 동일한 쿼리를 여러 번 실행해도 항상 동일한 결과를 보장합니다.
- SERIALIZABLE: 가장 높은 수준의 격리로, 트랜잭션 간에 완전한 격리를 보장합니다.
@Transactional(isolation = Isolation.SERIALIZABLE) public void performWithHighIsolation() { // SERIALIZABLE 격리 수준으로 실행 }
- timeout (타임아웃):
- 트랜잭션의 최대 실행 시간을 초 단위로 설정합니다. 설정된 시간 안에 완료되지 않으면 트랜잭션은 자동으로 롤백됩니다.
@Transactional(timeout = 5) public void performWithTimeout() { // 5초 안에 완료되지 않으면 롤백됨 }
- readOnly (읽기 전용):
- 트랜잭션을 읽기 전용으로 설정합니다. 이 설정은 데이터 읽기만 수행하고 변경하지 않을 때 사용됩니다. 이를 통해 성능 최적화를 할 수 있습니다.
@Transactional(readOnly = true) public List<User> getAllUsers() { return userRepository.findAll(); }
- rollbackFor 및 noRollbackFor:
- 특정 예외에 대해 롤백 여부를 설정합니다.
- rollbackFor: 지정된 예외가 발생하면 롤백합니다.
- noRollbackFor: 지정된 예외가 발생하더라도 롤백하지 않고 커밋합니다.
@Transactional(rollbackFor = CustomException.class) public void performWithCustomRollback() throws CustomException { // CustomException 발생 시 롤백됨 }
728x90
4. @Transactional 동작 방식
- @Transactional은 AOP(Aspect-Oriented Programming)를 사용하여 트랜잭션 경계를 설정합니다. 이로 인해 트랜잭션 경계 설정을 위한 부가기능이 런타임 시점에 프록시 객체에 의해 관리됩니다.
- 이는 다음과 같은 방식으로 동작합니다:
- Spring은 @Transactional이 적용된 메소드를 호출할 때, 먼저 해당 메소드가 트랜잭션 내에서 실행되는지 확인합니다.
- 트랜잭션이 없다면 새로운 트랜잭션을 시작하고, 기존 트랜잭션이 있다면 설정된 전파 속성에 따라 기존 트랜잭션에 참여하거나 새로운 트랜잭션을 시작합니다.
- 메소드 실행 후 오류가 발생하지 않으면 커밋, 오류가 발생하면 롤백됩니다.
5. 주의사항 및 사용 시 유의점
- 프록시 기반 동작:
- Spring의 @Transactional은 프록시 객체를 통해 동작합니다. 자기 자신 클래스 내에서 트랜잭션 메소드를 호출하면 트랜잭션이 적용되지 않을 수 있습니다. 이는 프록시가 자기 자신에 대한 호출을 가로채지 못하기 때문입니다.
- 해결 방법으로는 별도의 서비스 클래스에 트랜잭션 메소드를 정의하거나, 자신 내 호출을 피하는 것이 있습니다.
- 프록시의 대상은 public 메소드:
- Spring의 기본 트랜잭션 관리 기법은 public 메소드에 대해서만 트랜잭션을 적용합니다. @Transactional이 적용된 메소드가 public이 아니면 트랜잭션이 적용되지 않습니다.
- 체크드 예외와 언체크드 예외:
- 기본적으로 런타임 예외(RuntimeException)가 발생할 때만 롤백이 자동으로 수행됩니다.
- 체크드 예외(CheckedException)에 대해 롤백을 원한다면 rollbackFor 속성을 사용하여 롤백을 지정해야 합니다.
- 읽기 전용 트랜잭션 최적화:
- readOnly = true를 설정하면 Hibernate는 SQL을 최적화하고, 플러시와 같은 동작을 피할 수 있어 성능이 개선됩니다. 그러나, 이 설정으로도 데이터베이스 엔진의 동작을 100% 제어할 수 있는 것은 아닙니다.
6. 실용적인 예제
@Service
public class BankService {
@Autowired
private AccountRepository accountRepository;
@Transactional(propagation = Propagation.REQUIRED)
public void transferMoney(Long fromAccountId, Long toAccountId, Double amount) {
Account fromAccount = accountRepository.findById(fromAccountId).orElseThrow();
Account toAccount = accountRepository.findById(toAccountId).orElseThrow();
fromAccount.withdraw(amount);
toAccount.deposit(amount);
// 이 코드가 완료되기 전에 문제가 생기면 전체 트랜잭션은 롤백됩니다.
accountRepository.save(fromAccount);
accountRepository.save(toAccount);
}
}
위 코드에서 transferMoney() 메소드는 트랜잭션 전파 유형이 REQUIRED이므로, 호출될 때 이미 트랜잭션이 있다면 해당 트랜잭션에 참여하고, 없으면 새 트랜잭션을 시작합니다. 만약 중간에 오류가 발생하면 모든 작업은 롤백됩니다.
728x90
'Spring.io' 카테고리의 다른 글
Spring Data JPA - 분산 트랜잭션 (0) | 2024.11.26 |
---|---|
Spring Data JPA - 특징 및 개발주의사항 (0) | 2024.11.26 |
Spring Data JPA - JpaRepository (0) | 2024.11.26 |
Spring Data JPA - EntityManager (0) | 2024.11.26 |
Spring Data JPA(Spring Data Java Persistence API) (0) | 2024.11.26 |
Comments