[JPA] 영속성 컨텍스트

enjoy89·2023년 11월 30일
1
post-thumbnail
post-custom-banner

영속성 컨텍스트(Persistence Context)

  • JPA(Java Persistence API)에서 사용되는 가장 중요한 용어
  • 엔티티(객체)를 영구 저장하는 환경이라는 뜻
  • 엔티티의 생명주기를 추적하고, DB와의 상호작용을 최적화하기 위해 사용된다.
  • JPA는 영속성 컨텍스트의 특징을 통해 엔티티를 객체(Object)로 취급하여 서로 연관된 엔티티를 조회(find) 해야될 때 FK를 통한 SELECT 쿼리를 2번 이상 발생시키거나 복잡한 JOIN 쿼리를 발생시키지 않고 객체(Object) 자체 끼리의 연관 관계를 맺어 보다 완벽한 객체 지향적인 프로그래밍을 할 수 있도록 지원한다.


특징

1차 캐시

  • 엔티티 객체를 저장하는 1차 캐시를 가지며, 이 캐시를 통해 엔티티 객체에 대한 반복적인 데이터베이스 조회 동작을 줄일 수 있다.
  • 엔티티 객체를 조회할 때, DB보다 먼저 1차 캐시를 탐색하여 해당 객체가 존재한다면 바로 반환하고, 존재하지 않다면 다음 순서로 DB을 탐색하여 객체를 반환한다.




동일성(identity) 보장

  • 영속성 컨텍스트는 동일한 엔티티에 대해 항상 동일한 인스턴스를 반환하여 객체 동일성을 보장한다.
  • DB가 아닌 애플리케이션 차원에서 제공하므로 일관된 객체 상태를 유지하는데 도움이 된다.
  • 객체 동일성을 보장한다는 말의 의미는 == 비교 연산자가 true임을 뜻한다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true




트랜잭션을 지원하는 쓰기 지연 (Transactional write-behind)

  • 트랜잭션을 commit 할 때까지 엔티티의 변경 사항을 모아두고 한번에 데이터베이스에 반영한다.
  • 버퍼링 기능 이라고 할 수 있다. commit 직전까지 INSERT 쿼리를 모아서 한번에 처리한다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // 1. [트랜잭션] 시작

em.persist(memberA);
em.persist(memberB);
// 2. memberA, memberB를 모두 1차 캐시에 저장한 후, INSERT SQL을 쓰기 지연 저장소에 쌓는다.
// 데이터베이스에 보내지 않은 상태

// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // 3. [트랜잭션] 커밋




변경 감지(Dirty Checking)

  • 엔티티 객체의 정보를 수정하고 update()persist() 코드 필요 없이 트랜잭션을 commit 하면 알아서 변경을 감지하고 UPDATE SQL을 생성하여 DB에 업데이트 정보가 반영된다.




지연 로딩(Lazy Loading)과 즉시 로딩(Eager Loading)

  • 지연 로딩은 연관된 엔티티를 실제로 필요한 시점에 로드하여 성능 개선에 도움이 된다.
  • 만약 자주 함께 사용되는 두 객체인 Member Team 가 있다고 가정해보자. 우리는 Member에 존재하는 하나의 대상을 조회하는 경우 Team 객체의 정보까지 항상 함께 조회해야 한다.
  • 이때 JPA 구현체는 가능하면 Member와 Team 객체 모두 JOIN을 사용해서 한번에 조회하고자 하는데, Team 객체의 fetch type을 Lazy 로 설정할 경우 엔티티 객체를 로딩하는 시점에 Team는 프록시 객체가 조회된다.
  • 여기서 프록시(Proxy) 객체대리라는 뜻으로, 실제 객체와 동일한 인터페이스를 제공하지만, 필요할 때만 실제 객체를 생성하거나 로드하여 그 기능을 대신 수행하는 객체를 의미한다.
  • 이처럼 Team 객체는 실제로 필요한 시점(필드값에 직접적으로 접근할 때)에 로드되어 불필요한 쿼리를 생성하지 않고도 Member 객체와 함께 조회되어 성능을 향상시킬 수 있다.
  • 즉시 로딩은 지연 로딩과 반대로 Member와 Team 객체를 한번에 조회한다.



지연 로딩과 즉시 로딩 사용 시 주의할 점

  • 실무에서는 가급적 지연 로딩만을 사용한다. 즉시 로딩을 사용하지 마라.

왜?

즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
N+1 문제란 개발자가 의도한 쿼리는 1개인데, 실제는 의도와 상관 없이 추가로 N개의 쿼리가 발생한다는 의미이다.
이는 대규모의 데이터를 조회할 때 치명적인 성능 문제를 일으킨다.

  • 즉시 로딩은 이처럼 상상도 못한 쿼리가 나갈 수 있으므로 사용하지 말자
  • 실제 내가 프로젝트를 진행하며 지연 로딩과 즉시 로딩의 문제를 실제로 직면한 상황을 나중에 추가하겠다.




Reference

[도서] 자바 ORM 표준 JPA 프로그래밍

profile
Backend Developer 💻 😺
post-custom-banner

0개의 댓글