[Spring] Persistence Context

이병수·2024년 1월 23일
0

스프링 정리

목록 보기
11/24
post-custom-banner

Persistence Context (영속성 컨텍스트)


Entity Manager

  • Persistence Context에 접근하여 Entity 객체들을 조작하기 위해서는 Entity Manager가 필요하다.

  • 이름 그대로, @Entity 어노테이션을 달고 있는 Entity 객체들을 관리하며 실제 DB 테이블과 매핑하여 데이터를 조회/수정/저장 하는 중요한 기능을 수행한다.

  • Entity ManagerPersistence Context 라는 논리적 영역을 두어, 내부적으로 Entity의 생애주기를 관리한다.

  • Entity Manager는 EntityManagerFactory를 통해 생성하여 사용할 수 있다.


EntityManagerFactory

  • EntityManagerFactory는 일반적으로 DB 하나에 하나만 생성되어 애플리케이션이 동작하는 동안 사용된다.

  • EntityManagerFactory를 만들기 위해서는 DB에 대한 정보를 전달해야한다.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("DB이름");
EntityManager em = emf.createEntityManager();

DB에 대한 정보를 토대로 JPA는 EntityManagerFactory를 생성하여 EntityManager를 생성할 수 있다.



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

Entity를 영구 저장하는 환경이라고 생각하면 된다.

  • 애플리케이션과 DB 사이에서 객체를 보관하는 가상의 DB 역할을 한다.

  • Entity Manager 를 통해 Entity를 저장하거나 조회하면 Entity ManagerPersistence ContextEntity를 보관하고 관리한다.


Entity 생애 주기

New (비영속)

Persistence Context와 전혀 관계가 없는 새로운 상태를 의미한다.

즉, 처음 생성되어 Entity Manager의 관리를 받지 않은 상태이다.
객체를 생성한 상태

Member member = new Member();
member.setId(1);
member.setUsername("엄준식");

Managed (영속)

Persistence Context에 관리되는 상태를 의미한다.

말 그대로, Entity Manager에게 관리되고 있는 상태이다.
EntityManager에서 persist() 메서드를 호출하게 되면 이 상태로 놓이게 된다.
persist() 메서드를 호출하게 되면 Persistence Context 내부에 데이터가 저장된 일종의 캐싱 상태에 가까우며, DB에 실제로 데이터가 저장된 것은 아니다.

EntityManager em = EntityManagerFactory.createEntityManager(); // Entity 매니저 생성
EntityTransaction et = em.getTransaction();	// EntityTransaction을 가져온다.

et.begin();		// 트랜잭션 시작

try {
	Member member = new Member();
    member.setId(1);
	member.setUsername("엄준식");
    
    em.persist(member);		// Persistence Context에 저장한다.
    
    et.commit();			// 오류가 발생하지 않고 정상적으로 하면 commit을 호출
} catch(Exception e) {
	et.rollback();	// 오류 발생 -> 롤백
}
...

Detached (준영속)

Entity Manager가 더 이상 관리하지 않은 상태로, Managed 상태에서 Entity Manager로 부터 분리된 상태이다.

em.detach(member);  // member 엔티티를 Persistence Context에서 분리
  • 특정 EntityDetached 상태로 전환한다.
em.clear();
  • Persistence Context 안을 완전히 초기화 한다.

    • Persistence Context 의 모든 EntityDetached 상태로 전환한다.

    • 그렇다고 Persistence Context 를 이용할 수 없는 것은 아닌, 내용이 비워진 거 뿐이다.

em.close();
  • Persistence Context 를 종료한다.

    • 해당 Persistence Context가 관리하던 Entity 들은 모두 Detached 상태 로 변경한다.

    • clear()와는 다르게 Persistence Context가 종료되어버려서 더이상 사용이 불가능하다.


Detached 상태에서 다시 Managed 상태로 변경하려면?

em.merge(member);
  • 전달받은 Entity를 사용하여 새로운 영속 상태의 Entity를 반환한다.

  • Persistence Context에 데이터가 없다면?

    • DB에 새롭게 조회를 해서 조회한 EntityPersistence Context에 저장한 뒤, Entity값을 사용하여 병합한다, 그 뒤에 Update SQL을 수행
  • 만약 DB에도 없다면?

    • 새롭게 생성한 EntityPersistence Context에 저장하고 DB에 Insert SQL을 수행

Removed (삭제)

Entity ManagerPersistence Context 에서 삭제된 상태이다.
Removed 상태가 되면 DB에 맵핑된 데이터도 함께 제거된다.

em.remove(member);


Persistence Context 기능

Persistence ContextEntity 객체를 효율적으로 쉽게 관리하기 위해 만들어진 공간이다.

어떻게 효율적으로 관리할 수 있을까?

  1. 1차 캐시

  2. 동일성(identity) 보장

  3. 쓰기 지연

  4. 변경 감지



1차 캐시

  • Persistence Context 는 내부적으로 캐시 저장소를 가지고 있다.

  • Persistence Context에 등록된 Entity 객체@Id 어노테이션이 붙은 필드값을 사용하여 Map<Id,Entity> 형태로 저장하게 된다.

  • 따라서 Persistence Context는 캐시 저장소 Key에 저장된 식별자 값을 사용하여 Entity 객체를 구분한다.

  • 어플리케이션에서 데이터 조회를 요청하면 EntityManager는 먼저 내부에 캐싱된 Entity가 있는지 확인을 한다.

  • 만약 데이터가 존재한다면 DB에 쿼리를 전송하지 않으며, 캐싱된 Entity를 반환한다.

  • 반대로 데이터가 존재하지 않으면 DB에 쿼리를 전송하여, 내부의 데이터를 캐싱한다.

  • 이를 통해 쿼리 성능을 최적화한다.


동일성(Identity) 보장

  • EntityManager 는 내부적으로 Entity를 저장한다.

  • 따라서 동일한 Entity를 요청할 때는 동일한 Entity를 반환함을 보장한다.


쓰기 지연 저장소(ActionQueue)

  • Entity Manager는 데이터 변경 시 반드시 트랜잭션을 시작해야 한다.

  • Entity Manager는 실제 트랜잭션이 커밋되기 전까지 발생한 쿼리를 모아서, commit() 이 호출될 때 한번에 전달한다.

    • 사실 commit() 후 추가적인 동작이 있는데, 바로 flush() 메서드의 호출이다.
    • flush() 메서드는 Persistence Context의 변경 내용들을 DB에 반영하는 역할을 수행한다.
    • 즉, 쓰기 지연 저장소의 SQL들을 DB에 요청하는 역할을 수행한다.
  • 만약 데이터를 처리하는 과정에서 의도치 않게 에러가 발생할 경우 roll-back에 용이하다.


추가

  • JPA가 트랜잭션처럼 SQL을 모아서 한번에 DB에 반영하는 것이 바로 쓰기 지연 저장소를 만들어 SQL을 모아두고 있다가 트랜잭션 commit후 한번에 DB에 반영을 한다.

변경 감지(Dirty Checking)

EntityManager em = EntityManagerFactory.createEntityManager();
EntityTransaction et = em.getTransaction();

et.begin();

Member member = em.find(Memo.class, "memo");
member.setUsername("memo1");
member.setContents("내용변경");

et.commit();
  • Entity Manager에 등록된 Entity에 수정이 발생하면, 트랜잭션이 커밋되는 시점에 1차 캐시 와의 비교를 통해 변경사항을 감지하여 Update 쿼리를 자동으로 실행한다.

  • 변경 감지에 대해 순서를 말하면

    1. Persistence Context의 1차 캐시에는 memo의 초기 데이터가 저장되어 있을 것이다.

    2. 이후 set 메서드를 통해 데이터를 변경한다.

    3. 트랜잭션 커밋 시 flush() 메서드가 호출되면서 1차 캐시에 엔티티와 비교하여 변경에 대한 감지를 한다.

    4. 이후 SQL Update 쿼리를 생성하여 쓰기 지연 SQL 저장소에 쿼리를 보낸다.

    5. DB에 저장된 데이터를 수정한다.

profile
백엔드 개발자가 되고 싶어요
post-custom-banner

0개의 댓글