[Spring] Persistence Context?

16020 단어 SpringSpring

0. Overview

JPA는 영속성(Persistence)을 관리함

영속성 ?
: 일반적으로 영속성 컨텍스트(Persistence Context)Entity를 영구히 저장하는 환경을 의미함

EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa");

// EntityManagerFactory를 통해 EntityManager 객체 생성
EntityManager em = emf.createEntityManager();

→ em, 즉 EntityManager로 Entity를 CRUD 처리하며,
이렇게 처리된 Entity는 DB에 곧바로 반영되는 것이 아니고, 영속성 컨텍스트에 보관, 관리됨

Book book = new Book();
// 필드 맵핑 setXxx() 생략..

em.persist(book); // 영속성 컨텍스트에 book Entity 저장 (DB가 아님)

영속성 컨텍스트는 EntityManager 생성 시 만들어지기 때문에 EntityManager를 통해 영속성 컨텍스트에 접근, 관리가 가능함.

em.persist(book); 를 통해 현재 영속성 context에 book이라는 entity가 들어가 있다.

그럼 언제 실제로 DB에 반영될까?


🧃 엔티티 생명 주기(Entity LifeCycle)

  • 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
    : Book book = new Book() 까지만 만들었을 때

  • 영속(managed): 영속성 컨텍스트에 저장된 상태
    : em.persist(book)

  • 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태

  • 삭제(removed): 삭제된 상태
    : em.remove()

persist()시 Managed라는 영속상태에 들어가고,
flush()는 영속 상태에 있는 entity를 DB에 보낼 때 사용한다.


1) 비영속 상태(new/transient) : 순수하게 엔티티 객체를 생성만 한 상태

아직 EntityManager를 통해 영속성 컨텍스트에 저장하지 않았음

Book book = new Book();
book.setBookName("어른왕자");

2) 영속 상태(managed) : EntityManager를 통해 Entity를 영속성 컨텍스트에 저장

→ 영속성 컨텍스트가 관리하는 Entity를 영속 상태(Managed)라고 함

em.persist()

3) 준영속 상태(detached) : 영속성 컨텍스트가 기존에 관리히던 Entity를 더 이상 관리하지 않을 경우 준영속 상태가 됨

// Book entity를 영속성 컨텍스트에서 분리, 영속 -> 준영속 상태
em.detach(book);

// 혹은
em.close() // 영속성 컨텍스트 닫기

// 혹은
em.clear() // 영속성 컨텍스트 초기화

4) 삭제 상태(removed) : Entity를 영속성 컨텍스트와 DB에서 삭제

em.remove(book);


🎂 영속성 컨텍스트의 특징

1) 영속성 컨텍스트는
Entity를 식별자(PK, 기본키, @Id로 Table의 기본 키와 맵핑한 값)로 구분한다.
-> 영속 상태는 반드시 식별자가 있어야한다.

2) 영속성 컨텍스트에 저장된 Entity는
Transaction이 Commit되는 순간 실제 DB에 반영되는데, 이를 flush라고 함


🍍 Entity CRUD

1) Read Entity(엔티티 조회)

영속성 컨텍스트 내부에는 캐시가 있는데, 이를 1차 캐시라고 함

영속 상태의 Entity는 모두 이곳에 저장됨 → Map 객체 상태로 저장된다.

Key : @Id
Value : Entity Instance

// 비영속
Book book = new Book();
book.setBookName("어른 왕자");

// 영속
em.persist(book);

→ @Id는 기본 키인 식별자 값이기 때문에 영속성 컨텍스트에서 데이터를 저장,
조회하는 기준은 기본키(Id)로 이루어짐

Entity 조회

Book book = em.find(Book.class, 1L);

→ em.find()가 호출되면 1차 캐시에서 Entity를 찾고, 찾고자 하는 Entity가 1차 캐시에 없을 경우 DB에서 조회하게 됨

즉, 영속성 컨텍스트를 먼저 보고 Entity를 조회한 뒤, 없는 경우 DB에서 조회한다.
이를 통해 성능이 좋아지고, 조회 시간을 감소할 수 있다.


1차 캐시에 찾고자 하는 Entity가 있을 경우 ?

id가 1L인 Entity가 1차 캐시에 존재하므로, DB까지 접근하지 않는다.


1차 캐시에 찾고자 하는 Entity가 없을 경우(데이터베이스에서 조회)

EntityManager는 DB를 조회해서 Entity를 생성, 1차 캐시에 저장한 후 영속 상태의 Entity를 반환함


영속 엔티티의 동일성 보장

식별자(Id)가 같은 Entity 인스턴스를 조회해서 비교

Book book = em.find(Book.class, 1L);
Book book2 = em.find(Book.class, 1L);

System.out.println(a == b); // ?

→ 결과는 true,
반복해서 find()를 호출해도 영속성 컨텍스트는 같은 Entity 인스턴스를 반환함.(Entity의 동일성 보장)

JDBC의 경우에는 서로 다른 인스턴스로 생성됨.


2) Insert Entity(엔티티 등록)

EntityManger는 트랜잭션을 commit하기 전 까지 DB에 Entity를 저장하지 않고, 내부 쿼리 저장소에 INSERT SQL을 모아둠.

트랜잭션이 commit될 때 모아둔 쿼리를 DB에 보냄(쓰기 지연, Transactional write-behind)

EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();

// EntityManager는 데이터 변경 시 트랜잭션을 시작해야 함
tx.begin();

em.persist(book1);
em.persist(book2);
// 영속성 컨텍스트에만 집어넣은 상태, DB에 들어가지 않은 상태
// 아직 Insert SQL을 수행하지 않음

// Commit되는 순간 DB에 Insert SQL을 보냄
tx.commit();

em.persist()를 통해 영속성 컨텍스트 내의 1차 캐시에 Entity를 저장하고, INSERT SQL 코드를 생성한다.

실제로 DB에 반영되는 순간은, tx.commit() 이 수행되었을 때이다.

DB에 접근할 때는 디스크를 돌리기 때문에 시간이 걸리는데, SQL문을 영속 컨텍스트에 모아놨다가 한번에 commit()하면 성능이 좋아진다.

트랜잭션이 commit되면 EntityManager는 영속성 컨텍스트를 flush() 함

→ flush()는 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 작업

즉, 등록, 수정, 삭제한 Entity를 DB에 반영.

→ 쓰기 지연 SQL저장소에 모인 쿼리를 DB에 보냄.

이렇게 영속성 컨텍스트의 변경 내역을 DB에 동기화한 후, 실제 DB의 트랜잭션을 commit()함.

→ SQL문을 DB에 한 번에 전달하기 때문에 성능 최적화가 가능


3) Update Entity(엔티티 수정)

JPA 미사용

DB의 데이터를 수정하는 SQL문

UPDATE BOOK
SET
	BOOK_NAME=?,
	AUTHOR=?
WHERE
	id=?

책의 발행 날짜 필드 추가 및 변경 기능 요구

→ 발행 날짜를 수정하는 별도의 SQL문 작성

UPDATE BOOK
SET
	PUB_DATE
WHERE
	id=?

→ 비즈니스 로직을 위해 SQL문을 일일이 확인해야 하는 번거로움, 복잡성 증가(비즈니스 로직이 SQL에 의존하게 됨)


JPA의 Entity 수정 방식 : 변경 감지(dirty checking)

→ em.update()같은 메서드는 없음, Entity의 변경 사항을 DB에 자동으로 반영하는 기능을 변경 감지라고 함.

EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();

tx.begin();

// 영속 상태의 Entity 조회
Book book = em.find(Book.class, 1L);

// 영속 상태의 Entity 데이터 수정
book.setBookName("어른 왕자");

tx.commit();

JPA는 Entity를 영속성 컨텍스트에 저장할 때,

최초 상태를 복사해서 저장해둠(스냅샷)

flush() 시점에 스냅샷과 Entity를 비교해서 변경된 Entity를 찾음


4) Delete Entity(엔티티 삭제)

Entity를 삭제하려면 먼저 삭제 대상의 Entity를 조회해야 함

// 삭제할 Entity 조회
Book book = em.find(Book.class, 1L);

em.remove(book); // 엔티티 삭제

→ DELETE SQL역시 쓰기 지연 SQL 저장소에 등록하고, 트랜잭션이 commit되어 flush()가 호출되면 실제 DB에 Query를 전달함.

좋은 웹페이지 즐겨찾기