영속성 관리 - 내부 동작 방식

LeeKyoungChang·2022년 3월 4일
0
post-thumbnail

자바 ORM 표준 JPA 프로그래밍 - 기본편 수업을 듣고 정리한 내용입니다.

 

📚 1. 영속성 컨텍스트

🔔 JPA에서 가장 중요한 2가지

  • 객체와 관계형 데이터베이스 매핑하기(ORM) : DB, 객체 등 설계와 관련된 부분이다.
  • 영속성 컨텍스트 : 실제 JPA가 내부적으로 어떻게 동작하는지와 관련된 부분

 

✔️ 엔티티 매니저 팩토리와 엔티티 매니저

스크린샷 2022-03-04 오후 3 00 41
  • JPA를 사용하기 위해서는 엔티티 매니저 팩토리와 앤티티 매니저에 대해 알아야 한다.
  • 웹 애플리케이션이 실행될 때 EntityManagerFactory가 생성된다.
  • 클라이언트의 요청이 올 때마다 EntityManage를 생성하여 커넥션 풀을 사용해서 DB에 접근한다.

 

📣 영속성 컨텍스트

  • JPA를 이해하는데 가장 중요한 용어
  • 뜻 : “엔티티를 영구 저장하는 환경”
  • EntityManager.persist(entity); : 영속성 컨텍스트에 엔티티를 저장한다는 것이다.
  • 영속성 컨텍스트는 논리적인 개념이며 눈에 보이지 않는다.
  • 엔티티 매니저를 통해서 영속성 컨텍스트에 접근한다.

 

📖 A. 엔티티의 생명주기

스크린샷 2022-03-04 오후 3 07 17
  • 비영속 (new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
  • 영속 (managed) : 영속성 컨텍스트에 관리되는 상태
  • 준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 삭제 (removed) : 삭제된 상태

 

✔️ 비영속

스크린샷 2022-03-04 오후 3 09 37
  • JPA에 관계없이 그냥 객체 생성, 초기화만 된 상태

 

✔️ 영속

스크린샷 2022-03-04 오후 3 09 51
  • 엔티티 매니저를 통해 영속성 컨텍스트에 접근한다.
  • 영속 상태가 된다고 해서 바로 DB에 쿼리가 날라가는 것은 아니다. → 이후에 커밋해야 저장된다.

 

✔️ 준영속, 삭제

스크린샷 2022-03-04 오후 3 10 04
  • 준영속 : 영속성 컨텍스트에서 삭제하는 것
  • 삭제 : 실제로 DB에서 해당 ROW를 삭제하는 것

 

📖 B. 영속성 컨텍스트의 이점

  • 1차 캐시
  • 동일성(identity) 보장
  • 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
  • 변경 감지(Dirty Checking)
  • 지연 로딩(Lazy Loading)

 

(1) 엔티티 조회, 1차 캐시

스크린샷 2022-03-04 오후 3 36 18
  • member 객체를 생성하고 persist하면 영속성 컨텍스트 내부의 1차 캐시에 저장된다.
  • 이 때 key-value로 저장되며, keymember1, valuemember 객체가 담긴다.

 

✔️ 1차 캐시에서 조회

스크린샷 2022-03-04 오후 3 36 31
  • JPA는 엔티티 조회할 때 1차 캐시부터 찾는다.
  • 1차 캐시에서 해당되는 키값을 가진 엔티티가 있다면, 1차 캐시에서 조회해온다.

 

✔️ 데이터베이스에서 조회

스크린샷 2022-03-04 오후 3 36 39
  • 1차 캐시에 없으면 DB에서 조회해서 member2 객체를 1차 캐시에 저장한다.
  • 그 다음 member2를 반환한다.

 

(2) 영속 엔터티의 동일성 보장

스크린샷 2022-03-04 오후 3 36 49
  • 1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공

 

(3) 엔티티 등록 트랜잭션을 지원하는 쓰기 지연

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

em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.

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

 

스크린샷 2022-03-04 오후 3 37 03
  • em.persist(memberA);memberA를 1차 캐시에 넣는다.
  • 1차 캐시에 저장됨과 동시에 JPA가 해당 엔티티를 분석해서 INSERT SQL 생성해서 쓰기 지연 SQL 저장소에 쌓아둔다.
  • em.persist(memberB);도 동일하게, 1차 캐시에 넣고 INSERT SQL 생성해서 쓰기 지연 SQL 저장소에 쌓아둔다.

 

스크린샷 2022-03-04 오후 4 00 15

transaction.commit();하는 시점에 쓰기 지연 SQL 저장소에 쌓인 SQL문들이 한꺼번에 날아간다. (flush)

✏️ 쓰기 지연을 사용하는 이유

  • 버퍼링 기능으로 인해 쿼리를 여러 번 날리지 않고 최적화가 가능하기 때문이다!

 

(4) 엔티티 수정 - 변경 감지(Dirty Checking)

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(); // [트랜잭션] 커밋
스크린샷 2022-03-04 오후 4 22 32

transaction.commmit();을 하면 내부적으로 flush가 실행된다.
→ 엔티티와 스냇샷을 비교해서 변경된 데이터를 감지한다.
(스냅샷: 엔티티 값을 읽어 온 최초 시점의 엔티티 데이터)
→ 변경 사항이 있다면 UPDATE SQL을 쓰기 지연 SQL 저장소 쌓아둔다.
→ 쓰기 지연 SQL 저장소의 UPDATE SQL을 DB에 반영하고 커밋한다.

 

(5) 엔티티 삭제

//삭제 대상 엔티티 조회
Member memberA = em.find(Member.class, “memberA");
						 
em.remove(memberA); //엔티티 삭제

 

📚 2. 플러시(Flush)

영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 것
➡️ 영속성 컨텍스트의 쿼리들을 데이터베이스에 날리는 것이다!

 

✔️ 플러시 발생

  • 변경 감지가 발생한다.
  • 수정된 엔티티 쓰기 지연 SQL 저장소에 등록한다.
  • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리)한다.

 

✔️ 영속성 컨텍스트를 플러시하는 방법

  • em.flush() : 직접 호출
  • 트랜잭션 커밋 : 플러시 자동 호출
  • JPQL 쿼리 실행 : 플러시 자동 호출

 

✔️ JPQL 쿼리 실행시 플러시가 자동으로 호출되는 이유

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();
  • JPA에서는 JPQL 쿼리를 수행하기 전에 flush를 실행해서 DB와 영속성 컨텍스트간에 동기화를 해준다.
    • 등록한 member들이 조회가 안되는 경우를 막는다.

 

✔️ 플러시 모드 옵션

em.setFlushMode(FlushModeType.COMMIT)
  • FlushModeType.AUTO : 커밋이나 쿼리를 실행할 때 플러시 (기본 값)
  • FlushModeType.COMMIT : 커밋할 때만 플러시

 

✏️ 플러시 정리

  • 영속성 컨텍스트를 비우지 않는다.
  • 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
  • 트랜잭션이라는 작업 단위가 중요하다. → 커밋 직전에만 동기화 하면 된다.

 

📚 3. 준영속 상태

  • 영속 → 준영속
  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)
  • 영속성 컨텍스트가 제공하는 기능을 사용 못한다.

 

✔️ 준영속 상태로 만드는 방법

  • em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
  • em.clear() : 영속성 컨텍스트를 완전히 초기화
  • em.close() : 영속성 컨텍스트를 종료

 


참고

profile
"야, (오류 만났어?) 너두 (해결) 할 수 있어"

0개의 댓글