[자바 ORM 표준 JPA 프로그래밍 - 기본편] 3. 영속성 관리-내부 동작 방식

jada·2024년 3월 10일
0

Spring 스터디

목록 보기
22/35

영속성 컨텍스트


  • 고객의 요청이 올 때마다 엔티티 매니저 팩토리를 통해 엔티티 매니저를 생성한다.
  • 엔티티 매니저는 내부적으로 데이터베이스 커넥션을 사용해서 DB를 사용하게 된다.

⭐ 영속성 컨텍스트 ?

  • JPA를 이해하는 데 가장 중요한 용어

    • "엔티티를 영구 저장하는 환경" 이라는 뜻
  • EntityManager.persist(entity);

    • 영속성 컨텍스트를 통해서 entity를 영속화한다는 뜻.
    • persist 메서드는 entity를 영속성 컨텍스트라는 곳에 저장한다.
  • 영속성 컨텍스트는 논리적인 개념 (눈에 보이지 않는다.)

  • 엔티티 매니저를 통해서 영속성 컨텍스트에 접근

엔티티의 생명주기

  • 비영속 (new/transient)

    : 객체를 생성한 최초의 상태. 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태.

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

    : 영속성 컨텍스트에 관리되는 상태

//객체를 생성한 상태(비영속) 
Member member = new Member(); 
member.setId("member1"); 
member.setUsername(“회원1);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);

엔티티 매니저 안의 영속성 컨텍스트에 멤버 객체가 들어가면서 영속 상태가 된다.

  • 준영속 (detached)
    : 영속성 컨텍스트에 저장되었다가 분리된 상태
  //회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태 
em.detach(member); 
  • 삭제 (removed)
    : 삭제된 상태
  //객체를 삭제한 상태(삭제) 
em.remove(member);

💙 영속성 컨텍스트의 이점

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

🔷 엔티티 조회, 1차 캐시

영속성 컨텍스트는 내부에 1차 캐시를 들고있다.

  • 1차 캐시의 key는 DB pk로 맵핑한 id이다. value(값)은 엔티티 객체 자체이다.

  • 1차 캐시에 엔티티가 있는 경우

    em.find(Member.class, "member1"); 로 조회를 하면 JPA는 영속성 컨텍스트에서 1차 캐시에서 member1 를 조회한다. 1차 캐시에 member1 엔티티가 있다면 DB에서 조회하지 않고, 1차 캐시에 있는 값을 그대로 조회해온다.

  • 1차 캐시에 엔티티가 없는 경우

    member2 객체가 1차 캐시에 없으면, DB를 조회한 후 DB에 있는 member2를 1차 캐시에 저장한다. 그리고 member2를 반환한다. 이후에 member2를 다시 조회하게 되면 영속성 컨텍스트 안의 1차 캐시에 있는 member2가 반환된다.

📍 고객 요청이 들어오고 해당 요청의 처리가 끝나면 해당 영속성 컨텍스트 및 1차 캐시는 없어진다. 즉 1차 캐시는 여러 고객이 공유해서 사용하는 캐시가 아니다. 한 트랜잭션 안에서만 효과가 있다. (애플리케이션 전체에서 공유하는 캐시는 JPA나 Hibernate 에서는 2차 캐시라고 한다.)

🔷 영속 엔티티의 동일성 보장

JPA가 영속 엔티티의 동일성을 보장해준다.

1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공

🔷 트랜잭션을 지원하는 쓰기 지연

em.persist(memberA); -> memberA가 1차 캐시에 들어간다. JPA가 이 엔티티를 분석해서 INSERT 쿼리를 생성해 쓰기 지연 SQL 저장소에 해당 쿼리를 쌓아둔다.

em.persist(memberB); -> 마찬가지이다.

transaction.commit();

쓰기 지연 SQL 저장소에 있는 쿼리들은, 트랜잭션을 커밋하는 시점에, flush가 되면서 DB에 날아간다. 그리고 실제 Database 트랜잭션이 commit 된다.

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


JPA는 Dirty Checking (변경 감지 기능)을 제공하여 엔티티의 데이터를 변경할 수 있도록 한다.
🤔 how? - 변경 감지 메커니즘

JPA에서는 트랜잭션을 커밋하면 1. flush가 발생한다.
2. JPA가 엔티티와 스냅샷을 비교한다. (스냅샷은 값을 읽어온 시점, 즉 영속성 컨텍스트의 1차 캐시에 들어온 최초 시점의 상태를 저장해둔 것이다.)
3. 엔티티와 스냅샷을 비교했을 때 바뀐 데이터들에 대해 UPDATE 쿼리를 만들어 쓰기 지연 SQL 저장소에 저장해둔다.
4. UPDATE 쿼리를 데이터베이스에 반영한다.
5. commit 한다.

🔷 엔티티 삭제

변경 감지 메커니즘과 똑같이 작동한다. (DELETE 쿼리가 나감.)



플러시 (flush)


플러시란, 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것이다.

데이터베이스 트랜잭션이 커밋되면 자동으로 플러시가 발생한다.

✅ 플러시가 발생하면?

  • 변경 감지

  • 수정된 엔티티 쓰기 지연 SQL 저장소에 등록된다.

  • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다. (등록, 수정, 삭제 쿼리)

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

  • em.flush() - 직접 호출

  • 트랜잭션 커밋 - 플러시 자동 호출

  • JPQL 쿼리 실행 - 플러시 자동 호출

(참고 - 플러시 모드 옵션)

  • em.setFlushMode(FlushModeType.COMMIT)
    • • FlushModeType.AUTO : 커밋이나 쿼리를 실행할 때 플러시 (기본값)
    • • FlushModeType.COMMIT : 커밋할 때만 플러시
    • 가급적 기본값으로 그냥 사용하는 것을 권장

💖 플러시는 !

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


준영속 상태


  • 영속 -> 준영속

  • 영속 상태의 엔티티가 영속성 컨텍스트에서 ❗ 분리(detached)

  • 영속성 컨텍스트가 제공하는 기능을 사용 못함

준영속 상태로 만드는 방법

• em.detach(entity)
특정 엔티티만 준영속 상태로 전환

• em.clear()
영속성 컨텍스트를 완전히 초기화

• em.close()
영속성 컨텍스트를 종료

profile
꾸준히 발전하는 개발자가 되자 !

0개의 댓글