JPA의 영속성 컨텍스트와 Entity 생명주기 모델

JINNI·2024년 5월 28일
0

[TIL] Java+Spring

목록 보기
12/15

영속성 컨텍스트(Persistence Context)

JPA에서 Entity를 영구 저장하고 관리하는 작업을 수행하는 공간
Java의 ORM, JPA는 무엇인가?

📖영속성 컨텍스트의 특징

  1. 영속성 컨텍스트에서는 Entity를 식별자로 구분
  2. JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터베이스에 반영하는데, 이를 플러시(flush)라고 함
  3. JPA에서는 EntityManager 로 Entity를 영속성 컨텍스트에서 관리
  • EntityManager는 Entity를 관리해 DB와 애플리케이션 사이에서 객체를 생성/수정/삭제하는 역할을 함
  • EntityManagerFactory는 엔티티 매니저를 만드는 곳
  • SpringBoot 내부에서 EntityManagerFactory를 하나만 생성해 관리하고 @PersistenceContext 또는 @Autowired를 통해 EntityManager를 사용
    @PersitenceContext
    EntityManager em;

Entity의 생명주기 모델

new, transient : Entity가 방금 인스턴스화되었으며 Persistence Context(PC)와 연결되지 않음. 할당된 식별자가 없고 데이터베이스와 관계 없는 상태

  • SQL INSERT문을 자동으로 수행하거나 변경 사항을 추적하지 않음.
  • 데이터베이스 및 JPA 관련 기능에 대한 연결이 없는 기본 Java 객체
    Author author = new Author();
    author.setFirstName("Thorben");
    author.setLastName("Janssen");
  • EntityManager.find()를 통해 Entity 개체는 managed로 변경되고 현재 PC에 연결됨

managed, persistent : Entity에 연관된 식별자가 있고 PC와 연관된 상태

  • Persistence Provider(e.g. Hibernate)가 객체의 변경을 감지하고, PC를 flush할 때 필요한 SQL INSERT 또는 UPDATE문을 생성
  • 방법1) 새 Entity 개체를 사용해 EntityManager.persist() 호출
    Author author = new Author();
    author.setFirstName("Thorben");
    author.setLastName("Janssen");
    em.persist(author);
  • 방법2) EntityManager.find(), JPQL Query, CriteriaQuery 또는 기본 SQL Query를 사용해 데이터베이스에서 Entity 개체를 로드할 수 있음
    Author author = em.find(Author.class, 1L);
  • 방법3) EntityManager.merge()를 호출해 분리된 Entity를 병합하거나 Hibernate Session에서 UPDATE 메소드를 호출해 업데이트
    em.merge(author);

detached : Entity에 연관된 식별자가 있지만 더이상 PC와 연관되지 않은 상태(일반적으로 PC가 닫혔거나, 인스턴스가 PC에서 쫓겨난 상황)

  • PC를 닫으면 Entity가 분리되며 이는 일반적으로 요청이 처리된 후에 발생함 → 데이터베이스 트랜잭션이 커밋 → PC가 닫히고 Entity가 호출자에게 반환 → 호출자가 detached 상태의 Entity 객체를 검색(지연 로딩 불가)
  • 관리되는 Entity를 분리해야 하는 경우는 거의 없으므로 보류 중인 변경 사항이 손실되지 않도록 먼저 PC를 flush하는 것이 필요함
    em.detach(author);

removed : Entity에 연관된 식별자가 있고 PC와 연관되어 있지만 데이터베이스에서 제거하도록 예약되어 있는 상태

  • EntityManager에서 제거 메소드를 호출해도 매핑된 데이터베이스 레코드가 제거되지 않으며, 수명 주기 상태만 removed로 변경됨
  • Persistence provider는 다음 flush 작업 동안 레코드를 제거하기 위해 SQL DELETE문을 생성함
    em.remove(author);

영속성 컨텍스트의 이점

영속성 컨텍스트를 기반으로 JPA가 제공하는 기능으로는 1차 캐시, 스냅샷, 동일성 보장, SQL 쓰기 지연, 변경 감지, 지연 로딩이 있다.

📖1차 캐시

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

  • 캐시는 Map의 형태로 이루어짐(key : @Id값, value : 해당 entity값)
  • 1차 캐시에서 Entity를 우선적으로 찾고, 해당 Entity가 있다면 바로 반환
  • 1차 캐시에 없는 경우 데이터베이스에서 조회
  • 캐시된 데이터를 조회할 때 DB를 거치지 않아도 되므로 매우 빠르게 데이터 조회 가능
Member member = new Member();
member.setId("member1");

em.persist(member);

Member findMember = em.find(Member.class, "member1"); 
Member findMember2 = em.find(Member.class, "member2");

📖Dirty Checking(변경 감지)

  • JPA에서 값을 변경할 때 Entity를 조회해 와서 Entity가 갖고 있는 필드값만 바꾸면 자동으로 UPDATE Query가 DB에 반영됨
  • 트랜잭션을 커밋하면, JPA가 변경되는 Entity 내용을 추적해 최종적으로 변경된 내용을 마지막에 DB에 Query를 날려 적용시키는 방식
  • 적당한 묶음으로 Query를 요청할 수 있으며 데이터베이스 시스템의 부담을 줄일 수 있게 됨
em.update(entity); // 이와 같은 업데이트 코드 없이
member.setId("member1"); // 이와 같이 변경하는 경우 자동 업데이트

🤔스냅샷?

변경 감지가 동작하는 이유는 1차캐시의 특징 중 하나인 스냅샷 덕분이다. 1차 캐시에 데이터를 등록해둘 때, Entity에 대한 초기 상태의 스냅샷을 저장해두는데 Entity가 변경되면 초기 상태의 스냅샷과 상태를 비교해 달라진 부분에 대한 Update Query가 최종적으로 DB에 반영된다.

📖Lazy Loading(지연 로딩)

  • Query로 요청한 데이터를 애플리케이션에 바로 로딩하지 않고 필요할 때 Query를 날려 데이터를 조회하는 것(↔조회할 때 Query를 보내 연관된 모든 Entity를 가져오는 즉시 로딩)
  • 지연 로딩을 사용하면 Entity의 연관 관계로 조회할 때 Proxy 객체를 활용(13번 글 참고)
  • @ManyToOne, @OneToOne → 즉시로딩이 디폴트
  • @OneToMany, @ManyToMany → 지연로딩이 디폴트

📖동일성 보장

  • Entity의 동일성을 보장 : id가 같은 2개의 Entity를 조회하면 같은 결과를 가짐
  • 동일성은 실제 인스턴스가 같은지 == 연산자로 비교 가능
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a==b); // 동일성 비교 True

📖트랜잭션을 지원하는 쓰기 지연

  • 트랜잭션을 커밋하기 전까지 실제로 DB에 질의문을 보내지 않고 Query를 모았다가 트랜잭션을 커밋하면 모았던 쿼리를 한번에 실행
  • 적당한 묶음으로 Query를 요청할 수 있으며 데이터베이스 시스템의 부담을 줄일 수 있게 됨
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();

transaction.begin();

em.persist(memberA);
em.persist(memberB);
// 여기까지 INSERT SQL을 DB에 보내지 않는다.

// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
// or entityManager의 flush()를 통해 쿼리 적용
transaction.commit(); // 트랜잭션 커밋


참고자료
33기 DO SOPT 서버 파트 3차 세미나 자료(배포 불가)
Chapter 3. Persistence Contexts
Entity Lifecycle Model in JPA & Hibernate
[JPA] Java Persist API 내부 동작 방식
스프링 데이터 JPA, 5분 만에 알아보기

profile
천재 개발자 되기

0개의 댓글