Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
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 31
Tags more
Archives
Today
Total
관리 메뉴

밤빵's 개발일지

[TIL]20240726 영속성 컨텍스트(Persistence) 본문

개발Article

[TIL]20240726 영속성 컨텍스트(Persistence)

최밤빵 2024. 7. 26. 22:15

영속성 컨텍스트(Persistence Context)는 JPA(Java Persistence API)에서 매우 중요한 개념으로, 데이터베이스와 애플리케이션 간의 데이터를 관리하는 역할을 한다. JPA를 이해하고 효과적으로 사용하기 위해서는 영속성 컨텍스트에 대한 이해가 필수인거같아서(스프링 강의에서 영속성컨텍스트 강의가 너무길고 집중이안되서 건너뛰었는데 후회중...ㅠ) 오늘 개발일지에서는 영속성 컨텍스트의 정의와 역할, 작동 방식, 장점, 그리고 주의사항 등을 정리해보았다. 

🤓영속성 컨텍스트(Persistence Context)란?

영속성 컨텍스트(Persistence Context)는 JPA(Java Persistence API)에서 엔티티(Entity) 객체를 저장하고 관리하는 메모리 상의 저장소이다. 영속성 컨텍스트는 데이터베이스와 애플리케이션 간의 중간 역할을 하며, 엔티티의 상태를 관리하고, 트랜잭션이 끝날 때 데이터베이스에 반영하는 역할을 한다. 

 

▶ 영속성 컨텍스트의 정의와 역할

영속성 컨텍스트는 엔티티 매니저(EntityManager)에 의해 관리되며, 엔티티의 생명주기를 관리한다. 엔티티는 크게 네 가지 상태를 가질 수 있다.

 

→ 비영속(New/Transient):

영속성 컨텍스트와 관계가 없는 상태로, 단순히 메모리에서 생성된 상태이다. 엔티티 객체를 생성한 직후의 상태.

  영속(Managed):

영속성 컨텍스트에 의해 관리되는 상태로, 해당 엔티티는 식별자(PK)를 가지고 있으며, 데이터베이스에 저장될 수 있는 상태.

  준영속(Detached):

한때 영속 상태였지만, 영속성 컨텍스트에서 더 이상 관리하지 않는 상태.

  삭제(Removed):

엔티티가 삭제된 상태로, 영속성 컨텍스트와 데이터베이스에서 삭제된다.

영속성 컨텍스트는 1차 캐시(First-Level Cache) 역할을 하며, 동일한 트랜잭션 내에서 동일한 엔티티에 대한 중복 조회를 방지하고 성능을 최적화한다. 엔티티가 영속성 컨텍스트에 이미 있는 경우, 데이터베이스를 조회하지 않고 1차 캐시에서 데이터를 반환한다. 

 

▶ 영속성 컨텍스트의 주요 기능

영속성 컨텍스트는 여러 가지 중요한 기능을 제공한다. 

 

→ 엔티티 관리(Entity Management):

영속성 컨텍스트는 애플리케이션 내의 엔티티 객체를 관리하고, 데이터베이스와의 동기화 상태를 유지한다. EntityManager의 persist(), merge(), remove() 등의 메서드를 사용하여 엔티티 상태를 조작할 수 있다.

변경 감지(Dirty Checking):

영속성 컨텍스트는 트랜잭션이 진행되는 동안 영속 상태의 엔티티의 변경 사항을 감지하여, 트랜잭션 종료 시점에 자동으로 데이터베이스에 반영한다. 개발자는 엔티티의 속성을 직접 변경만 하면, flush() 시점에 변경 사항이 반영된다.

1차 캐시(First-Level Cache):

영속성 컨텍스트는 같은 트랜잭션 내에서 동일한 엔티티에 대한 중복 조회를 방지하기 위해 1차 캐시를 사용한다. 예를 들어, 같은 엔티티를 두 번 조회할 경우, 두 번째 조회는 데이터베이스에 접근하지 않고 1차 캐시에서 결과를 반환한다.

쓰기 지연(Write-Behind):

엔티티 매니저가 persist() 메서드를 호출할 때, 영속성 컨텍스트는 실제 SQL 쿼리를 데이터베이스에 바로 전달하지 않고, 트랜잭션을 커밋하는 시점에 모아둔 SQL을 한번에 실행한다. 이를 통해 성능을 최적화하고, 여러 번의 데이터베이스 액세스를 줄일 수 있다.

동시성 관리(Concurrency Control):

영속성 컨텍스트는 데이터베이스의 동시성 문제를 관리하는 데 도움을 준다. @Version 필드와 함께 사용하여 낙관적 락(Optimistic Lock)을 구현할 수 있다.

 

▶ 영속성 컨텍스트의 작동 방식

영속성 컨텍스트는 엔티티의 생명주기를 관리하고, 변경된 엔티티 상태를 데이터베이스에 반영하는 중요한 역할을 한다. 아래는 영속성 컨텍스트의 작동 방식을 단계별로 설명하자면,

 

→ 엔티티의 영속화:

엔티티 객체를 new 키워드로 생성하면, 엔티티는 비영속 상태에 있다. EntityManager.persist(entity)를 호출하면, 엔티티는 영속 상태로 전환된다.

영속 상태로 전환된 엔티티는 영속성 컨텍스트의 1차 캐시에 저장되며, 데이터베이스에 즉시 저장되지 않고, 트랜잭션이 커밋되는 시점에 flush() 메서드를 통해 저장된다.

변경 감지와 플러시(Flush):

트랜잭션이 진행되는 동안, 영속성 컨텍스트는 관리하는 엔티티의 상태를 지속적으로 감지한다. 만약 엔티티의 필드가 변경되면, 이를 감지하여 데이터베이스에 반영할 준비를 하고, EntityManager.flush()를 호출하거나 트랜잭션을 커밋하면, 영속성 컨텍스트는 변경된 엔티티 상태를 데이터베이스에 반영한다. 

엔티티의 준영속 상태 전환:

EntityManager.detach(entity) 또는 EntityManager.clear()를 호출하면, 엔티티는 더 이상 영속성 컨텍스트에서 관리되지 않는 준영속 상태로 전환된다. 준영속 상태의 엔티티는 변경 사항이 더 이상 영속성 컨텍스트에 반영되지 않는다.

엔티티의 삭제:

EntityManager.remove(entity)를 호출하면, 해당 엔티티는 영속성 컨텍스트와 데이터베이스에서 모두 삭제된다. 

 

▶ 영속성 컨텍스트의 장점

영속성 컨텍스트는 데이터베이스와 애플리케이션 간의 효율적인 데이터 관리를 위해 중요한 역할을 한다. 그 주요 장점은 다음과 같다.

 

→ 성능 최적화:

1차 캐시를 통한 데이터 중복 조회 방지로 성능을 최적화할 수 있다.

변경 감지를 통한 자동 반영:

개발자가 직접 SQL을 작성하지 않아도, 변경 사항을 자동으로 데이터베이스에 반영할 수 있다.

트랜잭션 관리:

트랜잭션 내에서 엔티티의 상태를 관리하고, 일관된 데이터 처리를 보장한다.

쓰기 지연을 통한 최적화:

다수의 쿼리를 한 번에 처리하여 데이터베이스 성능을 향상시킨다.

 

▶ 영속성 컨텍스트 사용 시 주의사항

영속성 컨텍스트를 사용할 때 주의해야 할 몇 가지 중요한 사항이 있다:

 

→ 지연 로딩(Lazy Loading)과 관련된 성능 문제:

영속성 컨텍스트에서 지연 로딩을 사용할 때, 불필요한 SQL 쿼리가 발생할 수 있다. 이러한 문제를 방지하기 위해 FetchType을 적절히 설정하고, 필요한 경우 즉시 로딩(Eager Loading)으로 전환하는 것이 좋다.

→ 메모리 관리:

영속성 컨텍스트에 너무 많은 엔티티가 저장되면 메모리 문제가 발생할 수 있다. 특히, 대규모 데이터를 처리할 때는 영속성 컨텍스트를 주기적으로 clear() 하여 메모리를 관리해야 한다.

→ 준영속 상태의 엔티티:

엔티티가 준영속 상태로 전환되면, 더 이상 변경 사항이 데이터베이스에 반영되지 않기 때문에 주의가 필요하다.

 

▶ 영속성 컨텍스트의 예시 코드

▽ 간단한 예시 코드

import jakarta.persistence.*;

@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String username;
    private int age;

    // getters and setters
    public Long getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("example-unit");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

        try {
            tx.begin(); // 트랜잭션 시작

            // 엔티티 생성 및 비영속 상태
            Member member = new Member();
            member.setUsername("user1");
            member.setAge(30);

            // 엔티티를 영속 상태로 전환
            em.persist(member);

            // 엔티티 조회 (1차 캐시 사용)
            Member findMember = em.find(Member.class, member.getId());

            // 엔티티 수정 (변경 감지)
            findMember.setAge(31);

            // 트랜잭션 커밋 (flush, 변경 사항 반영)
            tx.commit();
        } catch (Exception e) {
            tx.rollback(); // 예외 발생 시 롤백
        } finally {
            em.close(); // 엔티티 매니저 종료
        }

        emf.close(); // 엔티티 매니저 팩토리 종료
    }
}

이 예시 코드는 JPA를 사용하여 영속성 컨텍스트에서 엔티티를 관리하는 방법을 보여준다. em.persist()를 통해 엔티티를 영속성 컨텍스트에 등록하고, em.find()를 통해 1차 캐시에서 엔티티를 조회한다. 변경 감지를 통해 엔티티의 변경 사항을 자동으로 데이터베이스에 반영한다.

 

▶ 정리

영속성 컨텍스트가 JPA에서 데이터의 일관성과 효율적인 관리를 위해 얼마나 중요한 역할을 하고있던건지 알게되었다. 영속성 컨텍스트는 데이터베이스와 애플리케이션 간의 중간 역할을 하며, 엔티티의 생명주기를 관리하고, 트랜잭션을 통해 데이터 일관성을 유지하는 데 필수적이다. 여태까지 개발일지를 쓰면서 여러번 이 비슷한 정보들을 봐왔는데 이제 조금 정리가 되는 느낌이다.