[JPA] 영속성 컨텍스트

leehyunjon·2022년 7월 15일
0

JPA

목록 보기
4/10

EntityManagerFactory

EntityManagerFactory

EntityManager를 생산하는 팩토리이다.

비용이 비싸기 때문에 일반적으로 하나의 EntityManagerFactory를 생성한다.

동시성을 보장하기 때문에 동시에 여러 스레드의 접근에도 안전하다. 다른 스레드간의 공유 가능.


EntityManager

JPA를 통해 엔티티를 영속성 컨텍스트에게 관리받게 할 수 있는 역할 수행.

비용이 저렴하기 때문에 하나의 트랜잭션당 하나의 EntityManger를 사용한다.

하지만 동시성을 보장하지 않기 때문에 스레드간의 공유되서는 안된다.

동시성 문제
간단하게 설명하면 다른 스레드에 의해 변경된 엔티티를 다른 스레드에서 적용하지 못할수도 있는 문제.


영속성 컨텍스트(Persistence Context)

영속성 컨텍스트란,

  • 엔티티를 영구저장하는 환경으로써 EntityManager를 생성할 때 하나씩 생성되며
  • 논리적 개념에 가깝다.
  • EntityManger를 통해 영속성 컨텍스트에 접근, 관리 할 수 있다.

엔티티 생명주기

엔티티의 4가지 상태

  • 비영속(new) : 영속성 컨텍스트와 관련없는 순수한 객체 상태
  • 영속(managed) : 영속성 컨텍스트에 의해 관리되는 상태
  • 준영속(detached) : 영속상태였다가 영속성 컨텍스트에서 분리된 상태
  • 삭제(removed) : 영속성 컨텍스트와 데이터베이스 상에서 삭제된 상태

비영속(new)

  • 영속성 컨텍스트와 관련없는 순수한 객체 상태
  • em.persist(member) 메소드 호출 이전 상태를 이야기 한다.

영속(managed)

  • 영속성 컨텍스트에 의해 관리되는 상태
  • em.persist(member) 메소드 호출 이후 상태를 이야기 한다.
  • em.find(), JPQL을 통해 조회를 한 엔티티도 영속상태

준영속(detached)

  • 영속 상태에서 영속성 컨텍스트와 분리가 된 상태를 이야기한다.
  • em.detached(member) : 객체를 영속성 컨텍스트와 분리
  • em.close() : 영속성 컨텍스트 종료
  • em.clear() : 영속성 컨텍스트 초기화

삭제(removed)

  • 영속성 컨텍스트와 데이터베이스에서 엔티티 삭제
  • em.remove(member) : 삭제

영속성 컨텍스트 특징

  • 1차 캐시
  • 동일성 보장
  • 쓰기 지연
  • 더티 체킹
  • 지연 로딩

1차 캐시

영속성 컨텍스트 내부에는 캐시를 가지고 있는데 이것을 1차 캐시라고 한다.

영속 상태의 엔티티를 모두 이곳에 저장하며 Map의 형태로 @Id를 식별자(key), 인스턴스를 값(value)로 저장한다.

em.persist(member)를 통해서 1차 캐시의 key에 member.idvalue에 member가 저장된다.

1차 캐시 특징

  • 엔티티를 조회하는 경우 데이터베이스에 바로 SELECT SQL을 요청하는 것이 아니라 1차 캐시에 해당 엔티티가 있는지 조회한다.

    • 1차 캐시에 엔티티가 존재한다면 1차 캐시에 존재하는 엔티티를 반환한다.

    • 존재하지 않다면 데이터베이스에 SELECT SQL을 요청하여 엔티티를 받아오고 해당 엔티티를 1차 캐시에 저장한 후 반환한다.

  • 1차 캐시는 하나의 트랜잭션 내에서만 효과가 있다.

동일성 보장

엔티티를 조회할 때 1차 캐시에 저장되어있는 엔티티를 가져온다고 했다.
그렇기 때문에 동일한 엔티티 조회를 요청했을 때 ==비교(동일성)이 보장된다.

Member member1 = em.find(Member.class, 1);
Member member2 = em.find(Member.class, 1);

member1 == member2 // true

쓰기 지연

엔티티를 저장하거나 삭제할 때, 영속성 컨텍스트는 트랜잭션이 commit()되기 전까지 INSERT, DELETE SQL을 데이터베이스에 전송하지 않는다.

INSERT, DELETE SQL은 쓰기 지연 SQL저장소에 저장해두었다가 트랜잭션이 commit()될 때 flush를 통해 데이터베이스에 저장해둔 SQL을 전송한다.

flush
영속성 컨텍스트에 있는 엔티티와 데이터베이스를 동기화하는 작업.

  • 직접 사용시, JPQL사용시 자동 사용, 트랜잭션 commit시 자동 사용.

더티 체킹

엔티티를 수정할 때, EntityManager는 update()와 같은 메소드를 지원해 주지 않는다. 그냥 없다.

flush시점에서 영속상태의 엔티티스냅샷과 비교하여 변경된 점이 있다면 UPDATE SQL을 생성하여 쓰기 지연 SQL저장소에 저장한 후 다른 저장되어있는 SQL과 함께 데이터베이스에 요청된다.

스냅샷
엔티티를 영속성 컨텍스트에 저장할 때, 최초 상태를 복사해서 저장한 것

JPA의 업데이트 기본 전략

JPA의 업데이트 기본 전략은 모든 필드를 업데이트한다.

그렇기 때문에 하나의 필드만 수정해도 모든 필드를 업데이트 한다.

  • 장점
    • 이전에 파싱해둔 쿼리를 재사용할 수 있다.
  • 단점
    • 데이터 전송량이 크다.

DynamicUpdate

수정하는 것에 비해 데이터 전송량이 너무 많다면 @org.hibernate.annotation.DynamicUpdate를 이용해 수정된 데이터만 동적으로 UPDATE SQL을 생성할 수 있다.

@Entity
@org.hibernate.annotation.DynamicUpdate
@Table(name = "Member")
public class Member {...}

지연 로딩

지연 로딩이란, 연관 객체를 실제로 사용하는 시점에 SELECT SQL을 데이터베이스에 요청하여 실제 데이터를 사용할 수 있게 하는 전략.

데이터를 효율적으로 조회하기 위한 전략

Member member = em.find(Member.class, 1); //SELECT MEMBER...

Team team = member.getTeam();
team.getName(); //SELECT TEAM;

조회하고자하는 엔티티와 함께 조회하길 원한다면 join으로 조회되는 즉시 로딩 전략도 있다.

Member member = em.find(Member.class, 1); //SELECT MEMBER JOIN TEAM ON...

Team team = member.getTeam();
team.getName();

이는 프록시에 대해 공부할때 자세하게 다루겠다.


트랜잭션 범위의 영속성 컨텍스트


Reference

https://ultrakain.gitbooks.io/jpa/content/chapter3/chapter3.7.html

https://gmlwjd9405.github.io/2019/08/06/persistence-context.html

https://github.com/hyunjong-96/SpringBoot_Study/blob/main/JPA/2.%EC%98%81%EC%86%8D%EC%84%B1%20%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8.md

profile
내 꿈은 좋은 개발자

0개의 댓글