[JPA/김영한] 영속성이란 무엇일까?

수영·2021년 9월 15일
2

JPA 공부!

목록 보기
2/9
post-thumbnail
post-custom-banner

이 글은 김영한님의 JPA 강의 중 3장을 듣고 정리한 내용입니다 :)
강의 : 자바 ORM 표준 JPA 프로그래밍 - 기본편
교재 : 자바 ORM 표준 JPA 프로그래밍🤷‍♀️

Jpa에서 중요한 것 2가지 ?

  1. 객제-디비 매핑
  2. 영속성 컨텍스트

이 장에서는 가장 중요한 2가지 중에 영속성 컨텍스트에 대해 알아보자-!

✅ 영속성 컨텍스트란

: 엔티티를 영구 저장하는 환경, 논리적인 개념
: 애플리케이션 - DB 사이에서 객체를 보관하는 가상의 DB 역할 (→ 플러시 시점에 DB에 반영)

엔티티매니저로 영속성 컨텍스트에 접근 (J2SE에서는 1:1관계로 생각, SpringBoot는 N:1)
이걸 쓰는 이유? 버퍼링이나 캐싱을 위해서 사용한다!


<엔티티의 생명주기 4가지>

  1. 비영속 상태 (new/transient)

객체만 생성하고 엔티티와 연결X

  1. 영속 상태 (managed) → @Id로 매핑된 키값이 있어야함

객체를 생성하고 저장한 상태,

영속성 컨텍스트가 관리하는 엔티티(1차캐시/쓰기지연저장소에 있는 상태)

ex) em.persist(member);

//객체를 생성만 한 상태(비영속)
Member member = new Member();
member.setId("member1");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

//객체를 저장한 상태(영속)
em.persist(member);
  1. 준영속 상태 (detached)

엔티티가 영속성 컨텍스트에서 분리 ex) em.detached( );

  1. 삭제 상태 (removed)

엔티티 삭제 ex) em.remove( );

잠깐, Status Change(상태 변화)는 크게 두 종류로 볼 수 있다.

  1. 비영속 → 영속 → 삭제
  2. 영속 → 준영속


👍 영속성 컨텍스트의 장점

1. 1차 캐시

엔티티 조회 시

  1. 1차 캐시에서 조회
  2. 1차 캐시에 없을 경우 DB에서 조회 (1차 캐시에 저장 후 반환)

이때, EM은 트랜잭션 단위이기에 트랜잭션이 끝나면 1차 캐시도 지워짐
⇒ 1차 캐시는 성능 향상도 있지만 매커니즘적인 장점이 큼

(참고로 2차 캐시란 애플리케이션 전체에서 공유하는 캐시이다)

2. 영속 엔티티의 동일성 보장 → 1차 캐시

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");

System.out.println(a == b) // 동일성 보장

같은 호출을 반복해도 1차 캐시에 있는 같은 엔티티 반환하여 동일성을 보장함
(JPA가 아닌 Mybatis는 동일성 보장이 안됨)

참고 - JPA 동일성 보장의 문제점?
(읽기 등급의 트랜잭션 격리 수준을 DB차원이 아닌 애플리케이션 차원에서 제공) → 16.1절에서

3. 엔티티 등록할 때 : 쓰기 지연

트랜잭션을 커밋하는 순간에 → 한 번에 디비에 SQL을 보냄
그 전에는 쓰기 지연 SQL 저장소에 모았다가 한번에 보냄!(성능을 위해서)

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);

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

4. 엔티티 수정할때 : 변경 감지(더티 체킹)

수정할 때, 개발자가 따로 업데이트나 저장 안하는 게 맞음

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);

//em.update(member) 이런 코드가 없어도 된다!
transaction.commit(); // [트랜잭션] 커밋

이때 내부 작동 방식을 보자면,
플러시 시점에 엔티티와 스냅샷을 비교해서 바뀐 엔티티 찾는데
바뀐 엔티티가 있으면→ update 쿼리를 쓰기 지연 저장소에 저장 → 디비에 반영

5. 지연 로딩 ⇒ 8장에서 자세히


💫 플러시하면~?

: 영속성 컨텍스트의 변경내용을 DB에 동기화 (이름으로 오해X, 삭제 아님!)
⇒ 방법 : em.flush()로 직접 호출 or 트랜잭션 커밋 or JPQL

플러시 발생하면

  • 변경 감지
  • 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록
  • 저장소의 모아둔 쿼리를 한 번에 DB에 보냄

1차 캐시랑은 상관 X, 쓰기지연 저장소를 한 번에 디비에 반영하는 과정임!
트랜잭션 커밋 직전에만 동기화하면 되니까 이런 매커니즘이 가능함

? 후에 추가 학습! ) 트랜잭션과 영속성 컨텍스트의 주기를 맞추는 게 중요함!
참고) 트랜잭션이란? DB의 상태 변화하는 작업 단위(단위는 개발자가 정함)


❗ 필요에 따라 준영속 상태로 만들 수도 있다→ 후에 보충

: 영속 상태의 엔티티가 영속성 컨텍스트에서 분리 em.detached( );

준영속 상태란, 1차 캐시랑 쓰기 지연 SQL 저장소에서 제거된 상태
⇒ 즉 트랜잭션 커밋할 때, 쿼리 생성이 안됨

영속상태란, JPA가 관리하는 상태, 1차 캐시에 있는 상태 ex) em.persist() , em.find( )

  • em.detach(entity)
    : 특정 엔티티만 준영속 상태로 바꿈

  • em.clear()
    : 1차 캐시 지우기, 그러면 영속성 컨텍스트 처음부터 만들어짐
    ⇒ 테스트 케이스 볼때 쿼리가 처음부터 작성되니까 확인용으로 유용함

  • em.close()
    : 영속성 컨텍스트 닫아서 관리X, 데이터 변경 안됨


profile
🎵🎵🎵🎶🎵
post-custom-banner

0개의 댓글