본문 바로가기
개발하자/🍃 SpringBoot

[자바 ORM 표준 JPA 프로그래밍 - 기본편] 03. 영속성 관리

by 밈밈무 2022. 9. 18.

영속성 컨텍스트

JPA에서 가장 중요한 2가지 중 하나로, 실제 JPA가 내부에서 동작하는 매커니즘을 말하는 것이 영속성 컨텍스트이다.

더보기

cf. JPA에서 가장 중요한 2가지

1. 객체와 관계형 데이터베이스 매핑하기(Object Relational Mapping) : 정적. DB와 객체 설계 어떻게 해서 어떻게 매핑시킬지, 설계 관련

2. 영속성 컨텍스트

- 엔티티 매니저 팩토리와 엔티티 매니저

 

엔티티 매니저 팩토리가 고객의 요청이 올 때마다 엔티티매니저를 생성하고 엔티티 매니저는 DB커넥션을 사용하여 DB를 사용한다.

- 영속성 컨텍스트의 개념

  • JPA를 이해하는 데 가장 중요한 용어로, 영속성 컨텍스트란 논리적인 개념으로 엔티티를 영구 저장하는 환경(context)이라는 뜻을 가지고 있다.
  • EntityManager.persist(entity); 를 통해 엔티티를 영속성 컨텍스트에 저장할 수 있다.
  • 엔티티 매니저를 통해 영속성 컨텍스트에 접근한다.
  • J2SE 환경의 경우 엔티티 매니저와 영속성 컨텍스트가 1:1이며, JSEE/스프링 프레임워크의 경우 N:1의 관계이다.

- 엔티티의 생명주기의 종류

  • 비영속 (new/transient) : 최초로 생성한 상태로, 영속성 컨텍스트와 전혀 관계 없는 새로운 상태
  • 영속 (managed) : 영속성 컨텍스트에 관리되는 상태(em.persist 실행한 후 영속 상태 됨)
  • 준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 삭제 (removed) : 삭제된 상태

- 비영속

객체가 영속 컨텍스트에 들어오지 않아 JPA와 관련이 없는 상태이다.

//객체를 생성한 상태
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");

- 영속

영속 컨텍스트에 들어가 영속 상태가 된다.

//비영속
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

//영속(객체를 저장)
em.persist(member); //DB에는 이후 transaction 커밋한 시점에 저장됨

- 준영속, 삭제

//준영속(회원 엔티티를 영속성 컨텍스트에서 분리)
em.detach(member);

//삭제(객체를 삭제한 상태)
em.remove(member);

- 영속성 컨텍스트의 이점

  • 1차 캐시
    • 먼저 DB가 아니라 1차 캐시를 조회한다.
//1차 캐시에 저장됨
em.persist(member);

//1차 캐시에서 조회(조회 시점에 1차 캐시에 없으면 DB에서 가져와 1차캐시에 저장한다.)
Member findMember = em.find(Member.class, "member1");

  • 동일성(identity) 보장
    • 1차 캐시로 반복가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 애플리케이션 차원에서도 제공한다.
    • 영속 엔티티의 동일성을 보장한다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");

System.out.println(a == b); //결과 : true
  • 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
    • JPA가 쓰기 지연 SQL 저장소에 쿼리를 쌓고 있다가 커밋하는 순간(transaction.commit();)에 데이터베이스에 쌓인 쿼리들을 보낸다.
  • 변경 감지(dirty checking)
    • setter이용하여 영속 엔티티 데이터를 수정만 해주면 em.update 와 같은 코드 없이 엔티티가 수정된다.
    • 스냅샷에 값을 최초로 읽어온 상태를 저장해 두고 엔티티와 스냅샷을 비교하기 때문에 가능하다.
  • 지연로딩(lazy loading)

- 플러시

보통 transaction commit될 때 일어나며, 영속성 컨텍스트의 변경내용을 데이터베이스에 반하는 것을 말한다. 즉, 영속성 컨텍스트의 쿼리들을 DB로 날리는 것이다.

  • 플러시 발생
    • 변경감지(dirty checking)
    • 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
    • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)
  • 영속성 컨텍스트를 플러시하는 법
    • em.flush() : 직접 호출하는 것으로, 강제로 flush를 일으키고자 할 때 사용한다.
    • 트랜잭션 커밋 : 플러시 자동 호출
    • JPQL 쿼리 실행 : 플러시 자동호출
      • 실제 DB에 쿼리를 날리지 않은 상태에서 중간에 JPQL을 실행하여 조회하려하면 DB에서 엔티티들을 가져올 수 없기 때문에 JPQL 쿼리 실행 시 플러시가 자동으로 호출된다.
      • (옵션으로 자동 호출 기능을 off할 수 있지만, 그냥 기본값대로 AUTO로 두는 것이 일반적)

- 준영속 상태

  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태(detached)
  • 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
  • 준영속 상태 만드는 법
    • em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
    • em.clear() : 영속성 컨텍스트 완전히 초기화
    • em.close() : 영속성 컨텍스트를 종료