본문 바로가기

Spring Boot

[Spring Boot] 엔티티 관리와 영속성 컨텍스트: Spring Data JPA와 @Transactional

728x90

개요

Spring Data JPA와 같은 ORM 프레임워크에서는 엔티티(Entity)를 관리하는 데 있어 영속성 컨텍스트(Persistence Context)가 중요한 역할을 합니다. @Transactional을 사용해왔지만 개념을 좀 더 잡기 위해 글을 작성하도록 했다.

 

 

영속성 컨텍스트란?

영속성 컨텍스트는 엔티티의 상태를 관리하는 JPA의 메커니즘입니다. 이는 엔티티를 저장하고, 수정하고, 삭제하는 등의 작업을 수행할 때 JPA가 사용하는 가상의 데이터 저장소라고 할 수 있습니다. 영속성 컨텍스트는 엔티티 매니저(EntityManager)에 의해 관리됩니다.

 

 

영속성 컨텍스트의 주요 역할

  1. 엔티티 저장: 데이터베이스에서 조회된 엔티티나 새로 생성된 엔티티를 영속성 컨텍스트에 저장.
  2. 변경 추적: 영속성 컨텍스트에 의해 관리되는 엔티티의 상태 변화를 추적.
  3. 동일성 보장: 동일한 트랜잭션 내에서 동일한 엔티티는 동일한 객체로 보장.

 

@Transactional 애노테이션

 

트랜잭션은 데이터베이스에서 일련의 작업들이 모두 성공하거나 모두 실패해야 하는 원자적 단위를 말합니다. 트랜잭션은 다음과 같은 ACID 특성을 가지고 있습니다:

 

  • Atomicity (원자성): 트랜잭션 내의 모든 작업이 성공하거나 모두 실패해야 합니다.
  • Consistency (일관성): 트랜잭션이 완료된 후 데이터베이스는 일관성 있는 상태를 유지해야 합니다.
  • Isolation (고립성): 동시에 실행되는 트랜잭션은 서로 간섭하지 않아야 합니다.
  • Durability (지속성): 트랜잭션이 성공적으로 완료되면 그 결과는 영구적으로 데이터베이스에 반영되어야 합니다.

 

@Transactional 애노테이션은 메서드나 클래스에 적용되어 트랜잭션의 시작과 종료를 관리합니다. 메서드가 실행될 때 트랜잭션이 시작되고, 메서드 실행이 완료되면 트랜잭션이 커밋되거나 롤백됩니다.

 

  1. 트랜잭션 관리: 트랜잭션 시작, 커밋, 롤백을 자동으로 처리.
  2. 영속성 컨텍스트 관리: 트랜잭션이 커밋될 때 영속성 컨텍스트의 변경 사항을 데이터베이스에 반영.

 

실행 순서

  1. 트랜잭션 시작: @Transactional 애노테이션이 있는 메서드가 호출되면 트랜잭션이 시작됩니다.
  2. 영속성 컨텍스트 활성화: 트랜잭션이 시작되면서 영속성 컨텍스트가 활성화됩니다.
  3. 엔티티 조작: 엔티티를 조회, 저장, 수정, 삭제 등의 작업을 수행합니다.
  4. 변경 사항 추적: 영속성 컨텍스트는 엔티티의 상태 변화를 추적합니다.
  5. 트랜잭션 커밋: 메서드 실행이 완료되면 트랜잭션이 커밋되고, 영속성 컨텍스트의 변경 사항이 데이터베이스에 반영됩니다.
  6. 트랜잭션 종료: 트랜잭션이 종료되고, 영속성 컨텍스트가 비활성화됩니다.

 

예제 코드

엔티티 클래스

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    // Getter, Setter, Constructor

    public User() {}

    public User(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

리포지토리 인터페이스

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

서비스 클래스

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 User createUser(String name) {
        User user = new User(name);
        return userRepository.save(user);
    }

    @Transactional
    public void updateUser(Long id, String newName) {
        User user = userRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("User not found"));
        user.setName(newName);
        // 변경 사항은 트랜잭션이 커밋될 때 자동으로 반영됨
    }
}

컨트롤러 클래스

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping
    public User createUser(@RequestParam String name) {
        return userService.createUser(name);
    }

    @PutMapping("/{id}")
    public void updateUser(@PathVariable Long id, @RequestParam String name) {
        userService.updateUser(id, name);
    }
}

 

 

마무리

Spring Data JPA에서의 엔티티 관리와 영속성 컨텍스트는 애플리케이션의 데이터 일관성을 유지하는 데 중요한 역할을 합니다.

 

@Transactional 애노테이션을 통해 메서드 실행 시 트랜잭션을 관리하고, 영속성 컨텍스트의 변경 사항을 자동으로 데이터베이스에 반영할 수 있습니다.

 

이를 통해 개발자는 데이터베이스 트랜잭션을 더 쉽게 관리하고, 코드의 가독성과 유지보수성을 높일 수 있습니다.