본문 바로가기
Programming/Database & Query

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

by 읽고 쓰는 개발자 2022. 1. 16.

JPA에서 가장 중요한 2가지

  • Mapping : 객체와 관계형 데이터를 각 어떻게 설계하여 매핑할 것인가. (설계 관련. 정적인 것)
  • 영속성 컨텍스트

영속성 컨텍스트 : Entity를 영구 저장하는 환경

  • Entitymanager.persist(entity): DB에 저장하는 메소드가 아닌, 영속성 컨텍스트에 저장하는 메소드!
  • 논리적인 개념. 가시적이지 않음.
  • 엔티티 매니저를 통해 영속성 컨텍스트에 접근
  • 엔티티 생명주기

영속성 컨텍스트의 이점

  • 1차 캐시 ( 하나의 트랜젝션에서의 캐시)
    • 영속성 컨텍스트 내부의 캐시
    • cf) 2차 캐시 : Application 전체에서 공유하는 캐시
  • 동일성(identity) 보장 : 1차 캐시로 반복 가능한 읽기 (repeatable read) 등급의 트랜젝션 격리 수준을 데이터 베이스가 아닌 애플리케이션 차원에서 제공
    • 동일성과 동등성
      • 동일성 (identity) : 실제 인스턴스가 같다. 따라서 참조 값을 비교하는 == 비교의 값이 같다.
      • 동등성 (equality) : 실제 인스턴스는 다를 수 있지만 인스턴스가 가지고 있는 값이 같다. 자바에서 동등성 비교는 equals() 메소드를 구현해야 한다.
  • 트랜젝션을 지원하는 쓰기 지연 ( transactional write-behind)
    • transaction commit하는 순간에 SQL 전송
    • 영속 컨텍스트 (EntityManeger)에는 1차 캐시 이외의 쓰기 지연 SQL 저장소가 존재.
    • insert 작업 flow
      1. 1차 캐시와 쓰기 지연 저장소에 저장됨 (DB 전송 X) → 2. flush (DB 전송) → 3. commit
      batch : insert query 한 번에 전송 가능!
    • update 작업 flow
      1. 변경된 entity가 persist → 2. 엔티티 정보 변경 감지 (dirty check) : 엔티티와 스냅샷을 비교하여 쓰기 지연 SQL 저장소에 쿼리 저장 → 3. UPDATE flush 4. commit
      필드가 많거나 저장되는 내용이 너무 크면 수정된 데이터만 사용해서 동적으로 UPDATE SQL을 생성하는 전략을 선택하면 된다. 단 이떄는 하이버네이트 확장 기능을 사용해야 한다. @org.hibernate.annotations.DynamicUpdate 어노테이션을 사용하면 수정된 데이터만 사용해서 동적으로 UPDATE SQL 생성. (@DynamicInsert : 데이터가 존재하는 필드만 insert sql 동적 생성)
  • 변경 감지 (Dirty Checking)
  • 지연 로딩 (Lazy Loading)

플러시 : 영속성 컨텍스트의 변경내용을 데이터베이스에 반영

  • Application과 DB의 데이터를 맞추는 작업 (동기화)
  • 플러시 작동해도 1차캐시는 지워지지 않음
  • 플러시 발생하면 일어나는 일
    • 변경 감지(dirty checking)
    • 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록
    • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송
    • !!플러시가 발생한다고 쿼리를 DB에 반영(commit) 아니라, transaction commit에 쌓아둔 쿼리가 DB에 전송되는 것
  • 영속성 컨텍스트를 플러시하는 방법
    • em.flush() → 직접 호출
    • 트랜젝션 커밋 → 플러시 자동 호출
    • JPQL 쿼리 실행 → 플러시 자동 호출
      • JPQL 쿼리 실행 시 플러시가 자동으로 호출되는 이유 : 영속성 컨텍스트에 저장된 이전 데이터/쿼리가 DB에 저장되지 않은 상태에서 JPQL의 쿼리문으로 DB데이터 조회 결과의 차이가 생길 수 있기 때문에 JPQL 쿼리 발생 이전에 flush 해야 함!
  • 플러시 모드 옵션
    • FlushModeType.AUTO : 커밋이나 쿼리를 실행할 때 플러시 (기본값)
    • FlushModeType.COMMIT : 커밋할 때만 플러시 ( 굳이 옵션 변경하지 않는 것이 좋음)
  • 플러시 특징
    • 영속성 컨텍스트를 비우지 않음
    • 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
    • 트랜잭션이라는 작업 단위가 중요 → 커밋 직전에만 동기화하면 됨

준영속 상태

  • 영속 → 준영속
  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태 (detached)
  • 영속성 컨텍스트가 제공하는 기능을 사용하지 못함
  • 준영속 상태로 만드는 방법
    • em.detach(entity) : 특정 엔티티만 준영속 상태로 전환. 1차캐시 제거, 쓰기 지연 SQL 저장소의 관련 SQL 제거
    • em.clear() : 영속성 컨텍스트를 완전히 초기화 (1차 캐시 등도 모두 초기화됨)
    • em.close() : 영속성 컨텍스트를 종료 (데이터 변경 등의 관리가 전혀 안되는 상태)
  • 직접 쓸 일은 거의 없음
  • 준영속 상태의 특징
    • 거의 비영속 상태에 가깝다. : 영속성 컨텍스트에서 관리하지 않기에 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩을 포함한 영속성 컨텍스트가 제고하는 어떠한 기능도 동작하지 않는다.
    • 식별자 값을 가지고 있다. : 비영속 상태는 식별자 값이 없을 수도 있지만, 준영속 상태는 이미 영속 상태였으므로 반드시 식별자 값을 가지고 있다.
    • 지연 로딩 불가
  • 병합 merge(Entity) : 준영속 상태의 엔티티를 다시 영속 상태로 변경
    • 준영속 상태의 엔티티를 받아서, 그 정보로 새로운 영속 상태의 엔티티 반환
    • 준영속 / 비영속 두 상태에서 쓰임

 

출처 : 자바 ORM 표준 JPA 프로그래밍-기본편 (김영한), 자바 ORM 표준 JPA 프로그래밍(김영한 저)