JPA

3장. 영속성

오초의 기록 2022. 2. 10. 12:00

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

  • 엔티티 매니저 : 엔티티와 관련된 모든 일(저장, 수정, 삭제, 조회)을 처리한다. 
  • 공장 만들기는 비용이 많이 들지만 공장에서 매니저 생성은 비용이 거의 들지 않는다. 
  • 엔티티 매니저 팩토리는 여러 스레드가 동시에 접근해도 안전하므로 서로 다른 스레드 간에 공유해도 되지만, 엔티티 매니저는 여러 스레드가 동시에 접근하면 동시성 문제가 발생하므로 스레드 간에 절대로 공유하지 않는다. 
  • 엔티티 매니저는 데이터베이스 연결이 꼭 필요한 시점까지 커넥션을 얻지 않는다. 보통 트랜잭션을 시작할 때 커넥션을 얻는다.

 

영속성 컨텍스트(persistence context)란? 

  •  개념 
    • 엔티티를 영구 저장하는 환경
    • 엔티티 매니저로 영속성을 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다. 

 

엔티티의 생명주기

 

 

    • New / transient (비영속) : 영속성 컨텍스트와 전혀 관계가 없는 상태
    • Managed (영속) : 영속성 컨텍스트에 저장된 상태
    • Detached (준영속) : 영속성 컨텍스트에 저장되었다가 분리된 상태

 

영속성 컨텍스트의 특징

  • 영속성 컨텍스트와 식별자 값 
    • 영속성 컨텍스트는 엔티티를 식별자 값(@Id로 테이블의 기본키와 매핑한 값)으로 구분한다. 
    • 영속 상태는 식별자 값이 반드시 있어야 한다. 
  • 영속성 컨텍스트와 데이터베이스 저장
    • JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터 베이스에 반영하는데 이것을 플러쉬(flush)라고 한다. 
  • 영속성 컨텍스트가 엔티티를 관리할 때 장점
    • 1차 캐시 , 동일성 보장 , 트랜잭션을 지원하는 씍 지원 , 변경 감지 , 지연 로딩

 

엔티티 조회

  • 1차캐시
    • 영속성 컨텍스트 내부에 캐시를 가지고 있는데 이것을 1차 캐시라고 한다. 
    • 영속 상태의 엔티티는 모두 이곳에 저장한다. 
    • @Id로 매핑한 식별자(데이터베이스의 기본키와 매핑)는 Map의 key 역할을 한다. 
    • Entity 인스턴스는 value 역할을 한다. 
    • 순서
      • 1차 캐시 조회 
      • 없으면 데이터 베이스 조회 
      • 조회한 데이터로 엔티티 생성해서 1차 캐시에 저장한다.(영속화)
      • 조회한 엔티티를 반환한다. 
    • 영속성 컨텍스트는 성능상 이점과 엔티티 동일성 보장 (a == b) 
      • 1차 캐시에 있는 같은 엔티티 인스턴스 반환

 

엔티티 등록

  • 쓰기 지연(transactional write-behind) 
    • 엔티티 매니저는 트랜잭션을 커밋하기 직전까지 데이터베이스에 엔티티를 저장하지 않고 내부 쿼리 저장소(쓰기 지연 SQL 저장소)에 SQL을 차곡차곡 모아둔다. 
    • 트랜잭션을 커밋할 때 모아둔 쿼리를 데이터베이스에 보내는데 이것을 트랜잭션을 지원하는 쓰기 지원이라고 한다. 
      • 커밋 직전에만 SQL을 데이터베이스에만 보내면 문제없다. 
    • 트랜잭션 커밋 → 엔티티 매니저가 트랜잭션 flush(플러시) 
      • 쓰기 지연 SQL 저장소에 모인 쿼리를 데이터 베이스에 보낸다. 
      • flush(플러시) : 영속성 컨텍스트의 변경 내용을 데이터 베이스에 동기화하는 작업

 

엔티티 수정

  • 수정 쿼리를 직접 작성할 때 문제점 
    • 수정 쿼리가 많아지고, 비즈니스 조직을 분석하기 위해 SQL을 계속 확인해야 한다. 결국 직접적이든 간접적이든 비즈니스 로직이 SQL에 의존하게 된다. 
  • 변경 감지 
    • 엔티티의 변경사항을 데이터 베이스에 자동으로 반영하는 기능
    • 스냅샷
      • JPA는 엔티티를 영속성 컨텍스트에 보관할 때 최초 상태를 복사해서 저장하는 것을 스냅샷이라고 한다. 
      • 플러시 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다.
    • 수정 순서 
      • 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시(flush)가 호출된다. 
      • 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾는다.
      • 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보낸다. 
      • 쓰기 지연 저장소의 SQL을 데이터베이스에 보낸다. 
      • 데이터 베이스 트랜잭션을 커밋한다. 
    • 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용한다. (비영속, 준영속 x) 
      • JPA는 엔티티의 모든 필드를 업데이트한다.

 

엔티티 삭제

  • remove() 
    • 우선 조회 후 삭제 
      • 삭제 쿼리를 쓰기 지연 SQL 저장소에 등록 후 트랜잭션 커밋해서 플러쉬 호출 시 삭제 데이터베이스에 삭제 쿼리를 전달한다. 

 

플러쉬 

  • 개념
    • 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
  • 방법
    • em.flush() 직접 호출
    • 트랜잭션 커밋 시 플러쉬가 자동 호출된다. 
    • JPQL 쿼리 실행 시 플러쉬가 자동 호출된다. 
  • 주의점
    • 플러시라는 이름으로 인해 영속성 컨텍스트에 보관된 엔티티를 지운다고 생각하면 안 된다. 다시 한번 강조하지만 영속성 컨텍스트의 변경 내용을 데이터 베이스에 동기화하는 것이 플러시다. 

 

준영속

  • 개념 
    • 영속성 컨텍스트가 관리하는 영속상태의 엔티티가 영속성 컨텍스트에서 분리된(detached)된 상태. 
    • 준영속 상태의 엔티티는 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다. 
  • 방법
    • em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
    • em.clear() : 영속성 컨텍스트를 완전히 초기화 
    • em.close() : 영속성 컨텍스트를 종료 
  • 특징
    • 거의 비영속 상태에 가깝다. 
    • 식별자 값을 가지고 있다. 
    • 지연 로딩할 수 없다. 
  • 병합(merge) 
    • 개념
      • 준영속 엔티티 → 영속 상태(새로운) 
      • 병합은 준영속, 비영속인지 신경 쓸 필요가 없다. 식별자 값으로 엔티티를 조회할 수 있으면 불러서 병합하고 조회할 수 없으면 새로 생성해서 병합한다. 따라서 병합은 save or update 기능을 수행한다.