JPA에서 트랜잭션 처리 및 데이터베이스 격리 수준

2024. 7. 19. 15:55·개발 노트/Spring
  1.  

트랜잭션이란?

트랜잭션(Transaction)은 데이터베이스의 상태를 변화시키기 위해 수행하는 일련의 연산들을 말한다. 트랜잭션은 데이터의 무결성과 일관성을 보장하기 위해 ACID 속성을 따른다.

  • 원자성(Atomicity): 트랜잭션 내의 모든 작업이 완벽하게 수행되거나 전혀 수행되지 않아야 한다.
  • 일관성(Consistency): 트랜잭션이 성공적으로 완료되면 데이터베이스는 일관성 있는 상태를 유지해야 한다.
  • 격리성(Isolation): 트랜잭션이 실행되는 동안 다른 트랜잭션의 영향을 받아서는 안 된다.
  • 지속성(Durability): 트랜잭션이 완료되면 그 결과는 영구적으로 반영되어야 한다.

 

JPA와 트랜잭션 처리

JPA에서 트랜잭션 관리는 주로 EntityManager를 통해 이루어진다. 하지만 Spring Data JPA를 사용하면 더 간단하게 트랜잭션을 관리할 수 있다. Spring에서는 @Transactional 어노테이션을 사용하여 트랜잭션을 선언적으로 관리할 수 있다.

@Transactional 어노테이션

@Transactional은 트랜잭션 범위를 설정하고 관리하는 데 사용되는 어노테이션이다. 메소드나 클래스 수준에서 적용할 수 있다.

@Service
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Transactional
    public void updateUser(Long id, String newUsername) {
        User user = userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
        user.setUsername(newUsername);
        userRepository.save(user);
    }
}
  • @Transactional: 메소드나 클래스에 적용하여 해당 범위 내에서 트랜잭션을 관리한다.

@Transactional에서 사용되는 어노테이션

  1. @Transactional(propagation = Propagation.REQUIRED): 기본 전파 수준으로, 현재 트랜잭션이 존재하면 해당 트랜잭션을 사용하고, 없으면 새로운 트랜잭션을 생성한다.
@Transactional(propagation = Propagation.REQUIRED)
public void saveNewUser(User user) {
    userRepository.save(user);
}
  1. @Transactional(readOnly = true): 읽기 전용 트랜잭션을 설정하여 성능을 최적화한다.
@Transactional(readOnly = true)
public User getUser(Long id) {
    return userRepository.findById(id).orElse(null);
}
  1. @Transactional(rollbackFor = Exception.class): 특정 예외가 발생했을 때 트랜잭션을 롤백한다.
@Transactional(rollbackFor = Exception.class)
public void riskyOperation() throws Exception {
    // 예외가 발생하면 트랜잭션 롤백
}
  1. @Transactional(isolation = Isolation.SERIALIZABLE): 트랜잭션의 격리 수준을 설정한다.
@Transactional(isolation = Isolation.SERIALIZABLE)
public void performCriticalOperation() {
    // 중요한 데이터베이스 작업 수행
}

트랜잭션 전파

트랜잭션 전파는 트랜잭션이 다른 트랜잭션 메소드를 호출할 때 어떻게 동작할지를 정의한다. Spring은 다음과 같은 전파 옵션을 제공한다:

  • REQUIRED (기본값): 현재 트랜잭션이 존재하면 해당 트랜잭션을 사용하고, 없으면 새로운 트랜잭션을 생성한다.
  • REQUIRES_NEW: 항상 새로운 트랜잭션을 생성한다. 기존 트랜잭션은 일시 중단된다.
  • MANDATORY: 현재 트랜잭션이 존재해야 한다. 존재하지 않으면 예외를 발생시킨다.
  • SUPPORTS: 현재 트랜잭션이 존재하면 해당 트랜잭션을 사용하고, 없으면 트랜잭션 없이 실행한다.
  • NOT_SUPPORTED: 트랜잭션 없이 실행한다. 기존 트랜잭션은 일시 중단된다.
  • NEVER: 트랜잭션이 존재하면 예외를 발생시킨다.
  • NESTED: 중첩된 트랜잭션을 생성한다.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveNewUser(User user) {
    userRepository.save(user);
}

 

트랜잭션 격리 수준

데이터베이스 트랜잭션 격리 수준은 트랜잭션이 다른 트랜잭션에 얼마나 영향을 받는지를 정의한다. 격리 수준은 다음과 같다:

  • READ UNCOMMITTED: 다른 트랜잭션이 커밋되지 않은 데이터를 읽을 수 있다.
  • READ COMMITTED: 다른 트랜잭션이 커밋한 데이터만 읽을 수 있다.
  • REPEATABLE READ: 트랜잭션이 시작된 이후에 커밋된 데이터만 읽을 수 있다.
  • SERIALIZABLE: 가장 높은 격리 수준으로, 트랜잭션 간에 완전한 격리를 보장한다.
@Transactional(isolation = Isolation.SERIALIZABLE)
public void performCriticalOperation() {
    // 중요한 데이터베이스 작업 수행
}

격리 수준의 선택

  • READ UNCOMMITTED: 성능이 중요하고 일관성이 크게 중요하지 않은 경우 사용.
  • READ COMMITTED: 대부분의 애플리케이션에서 기본으로 사용.
  • REPEATABLE READ: 일관성이 중요한 애플리케이션에서 사용.
  • SERIALIZABLE: 데이터 일관성이 매우 중요한 경우 사용하지만, 성능 저하가 발생할 수 있다.

 

트랜잭션 롤백

@Transactional 어노테이션을 사용하면 특정 예외가 발생했을 때 트랜잭션을 롤백할 수 있다. rollbackFor 속성을 사용하여 롤백할 예외를 지정할 수 있다.

@Transactional(rollbackFor = Exception.class)
public void riskyOperation() throws Exception {
    // 예외가 발생하면 트랜잭션 롤백
}

 

트랜잭션 처리와 성능 최적화

트랜잭션 처리는 데이터베이스 성능에 영향을 미칠 수 있다. 트랜잭션 범위를 최소화하고, 필요한 경우 트랜잭션 격리 수준을 낮추어 성능을 최적화할 수 있다.

트랜잭션 범위 최소화

트랜잭션의 범위를 최소화하여 데이터베이스 잠금 시간을 줄이고 성능을 향상시킬 수 있다. 예를 들어, 필요한 최소한의 코드 블록만 트랜잭션 내에서 실행하도록 한다.

@Transactional
public void updateMultipleUsers(List<User> users) {
    for (User user : users) {
        userRepository.save(user);
    }
}

캐싱 사용

캐싱을 사용하여 동일한 데이터에 대한 반복적인 데이터베이스 접근을 줄일 수 있다. Spring과 Hibernate는 1차 캐시와 2차 캐시를 지원한다.

 

마무리

JPA와 Spring Data JPA를 사용하면 트랜잭션을 쉽게 관리할 수 있다. 트랜잭션 전파, 격리 수준, 롤백 설정 등을 통해 데이터의 일관성과 무결성을 유지하면서 성능을 최적화할 수 있고, 트랜잭션 관리는 데이터베이스와의 상호작용에서 매우 중요한 부분이다. 트랜잭션 관리 기능을 잘 이해하고 활용하는 것이 정말 중요할 것 같다.

'개발 노트 > Spring' 카테고리의 다른 글

JPA에서 N+1 문제  (2) 2024.07.19
Spring Data JPA 이해하기: 주요 구성 요소와 어노테이션  (0) 2024.07.19
JPA 이해하기: 동작 원리와 핵심 구성 요소  (0) 2024.07.19
'개발 노트/Spring' 카테고리의 다른 글
  • JPA에서 N+1 문제
  • Spring Data JPA 이해하기: 주요 구성 요소와 어노테이션
  • JPA 이해하기: 동작 원리와 핵심 구성 요소
악덕
악덕
우당탕탕 개발 블로그
  • 악덕
    버그와 함께 춤을
    악덕
  • 전체
    오늘
    어제
    • 전체 (26)
      • TIL (2)
      • 개발 노트 (19)
        • Java (5)
        • JavaScript (1)
        • Spring (4)
        • Linux (1)
        • DevOps (1)
        • etc. (7)
      • 문제 풀이 (0)
      • 삽질 로그 (3)
      • 기타 (2)
  • 링크

    • GitHub
    • Project.zip
    • 밀로(millo)
  • 태그

    웹개발
    프로그래밍
    프로그래밍언어
    Til
    Docker
    solid 원칙
    컨테이너
    JPA
    DevOps
    Spring Data JPA
    java persistence api
    gdg
    자료형
    mysql
    java
    springboot
    객체지향 프로그래밍
    ssl
    자바기초
    OOP
  • hELLO· Designed By정상우.v4.10.3
악덕
JPA에서 트랜잭션 처리 및 데이터베이스 격리 수준
상단으로

티스토리툴바