Skip to content

Latest commit

 

History

History
134 lines (111 loc) · 7.09 KB

[승재] 3장.md

File metadata and controls

134 lines (111 loc) · 7.09 KB

영속성 관리

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

  • 엔티티 매니저 팩토리 : 생성하는데 비용이 매우 크다. 그리고 여러 쓰레드가 동시에 접근해도 안전하므로 서로 스레드 간에 공유가 가능하여 보통 어플리케이션 내에 하나만 생성하여 사용한다.

  • 엔티티 매니저 : 엔티티 매니저 팩토리에서 생성되고 생성하는 비용이 거의 들지 않는다. 여러 쓰레드가 동시에 접근하면 동시성 문제가 발생하므로 쓰레드 간에 절대 공유하면 안된다.

영속성 컨텍스트란?

  • 영속성 컨텍스트 : 엔티티를 영구 저장하는 환경. 엔티티 매니저를 생성할 때 하나 만들어지며 엔티티 매니저를 통해 접근하고 관리되어진다.

엔티티의 생명 주기

  1. 비영속(new/transient) : 영속성 컨텍스트와 전혀 관계X
  2. 영속(managed) : 영속성 컨텍스트에 저장된 상태
  3. 준영속(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
  4. 삭제(removed) : 삭제된

=> 책의 그림을 보면 좀 더 이해하기 쉬우실 것 입니다!

영속성 컨텍스트의 특징

  1. 영속성 컨텍스트와 식별자 값
  2. 영속성 컨텍스트와 데이터베이스 저장
  3. 영속성 컨텍스트가 엔티티를 관리시 얻어지는 장점들
  • 1차 캐시
  • 동일성 보장
  • 트랜잭션을 지원하는 쓰기 지연
  • 변경 감지
  • 지연 로딩

엔티티 조회

  • 영속성 컨테스트 내부에는 캐시가 존재 -> 1차 캐시

  • 영속 상태 엔티티 모두 1차 캐시에 저장

  • 내부에 Map이 하나 존재, @Id로 매핑한 식별자가 Key, value가 엔티티 인스턴스

  • em.find() -> 1차 캐시에서 엔티티 조회 -> 없없을 경우 DB에서 조회

  • DB에서 조회시 조회하여 엔티티 생성하여 1차 캐시에 저장 -> 영속 상태의 엔티티 반환

  • 1차 캐시에 있을 경우 DB까지 갈 필요가 없기에 성능상 이점!

  • 1차 캐시를 사용하기에 반복해서 조회해도 1차 캐시에 있는 같은 엔티티 인스턴스 반환! => 동일성 보장

  • 참고 : 동일성 vs 동등성

  • 참고 : JPA는 1차 캐시를 통해 어플리케이션 차원에서 Repeatable Read 등급의 트랜잭션 격리 수준 제공

REPEATABLE READ : 반복해서 read operation을 수행하더라도 읽어 들이는 값이 변화하지 않는 정도의 isolation을 보장하는 level

Ref : https://suhwan.dev/2019/06/09/transaction-isolation-level-and-lock/

엔티티 등록

  • 트랜잭션 커밋하기 SQL을 쓰기 지연 SQL 저장소에 저장 -> 트랜잭션을 지원하는 쓰기 지연
  • 커밋 시에 영속성 컨텍스트를 플러시.
  • 플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 작업
  • 즉 쓰기 지연 SQL 저장소에 모인 쿼리를 DB에 보내서 영속성 컨텍스트의 변경 내용을 DB에 동기화 한후에 실제 DB 트랜잭션을 커밋하는 방식

엔티티 수정

  • 요구사항이 늘어나고 변경되며 수정 쿼리가 점점 추가되며 비즈니스 로직이 SQL에 의존되게 되는 문제가 발생
  • JPA는 이를 해결! -> update 메서드 존재 X
  • 변경 감지(dirty checking)를 사용
  1. 트랜잭션을 커밋시 엔티티 매니저가 flush()
  2. 엔티티와 스냅샷(영속성 컨텍스트에 보관한 엔티티의 최초의 상태) 비교 -> 변경된 엔티티 찾음
  3. 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL저장소에 보냄
  4. 쓰기 지연 저장소의 SQL을 DB로
  5. DB 트랜잭션 커밋
  • 추가 사항 : JPA는 변경된 부분만 UPDATE 쿼리를 날리는 것이 아닌 엔티티의 모든 필드를 업데이트
    • 모든 필드를 사용하면 수정쿼리가 항상 동일 -> 즉 어플리케이션 로딩 시점에 수정 쿼리를 미리 생성해주고 재사용
    • DB에 동일한 쿼리를 보내면 DB는 이전에 한 번 파싱된 쿼리를 재사용 가능
    • 물론 필드가 너무 많거나 저장되는 내용이 너무 크면 설정을 통해 수정된 데이터만 동적으로 UPDATE SQL 생성 가능
    • 근데 사실 30개 정도 이상되면 보통 동적으로 해주는게 좋다는데 그 정도 되면 그냥 테이블이 이상하게 아닌가...
    • INSERT SQL도 데이터가 존재하는(null X) 필드만으로 동적으로 생성하게 설정 가능

엔티티 삭제

  • remove()로 엔티티 넘겨줘서 엔티티 삭제
  • 물론 바로 삭제가 아닌 SQL에 저장소에 등록되고 커밋지에 플러시 호출되서 위처럼~
  • 이때 remove()를 호출하는 순간 해당 엔티티는 일단 영속성 컨텍스트에서 제거되므로 해당 엔티티 재사용 X -> 자연스럽게 GC의 대상이 되도록 두어라~

플러시

  • 플러시는 영속성 컨텍스트의 변경 내용을 DB에 반영
  1. 변경 감지 동작 -> 영속성 컨텍스트에 있는 모든 엔티티를 스냅샷과 비교 -> 수정된 엔티티 발견 -> 해당하는 수정 쿼리 만들어서 쓰기 지연 SQL 저장소에 등록
  2. 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송

플러시하는 방법

  1. em.flush()
  2. 트랜잭션 커밋 시 플러시가 자동 호출
  3. JPQL 쿼리 실행시 플러시가 자동 호출 -> JPQL은 SQL로 변환되어 DB에서 조회 -> 그렇기에 그 전에 DB 적용 안된 사항이 있으면 결과가 이상해짐 -> 따라서 JPQL 쿼리 실행시 플러시 호출

플러시 모드 옵션

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

추가 사항

  • 플러시라는 이름으로 인해 영속성 컨텍스트에 보관된 엔티티를 지운다고 생각 X
  • 그냥 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 것

준영속

  • 준영속 상태의 엔티티는 영속성 컨텍스트가 제공하는 기능 사용 X

준영속 상태로 만드는 법

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

준영속 상태의 특징

  1. 거의 비영속
  2. 식별자 값 가지고 있다.
  3. 지연 로딩 불가

병합: merge()

  • 준영속 상태의 엔티티를 받아서 그 정보로 새로운 영속 상태의 엔티티 반환
User merged = em.merge(user); // user는 준영속 그대로, merge는 영속
// 준영속 상태인 user는 이제 노쓸모
// 따라서 아래와 같이 쓰는 것이 좋음
user = em.merge(user);

추가 사항

  • 병합은 파라미터로 넘어온 엔티티의 식별자 값으로 영속성 컨텍스트 조회
  • 찾는 엔티티가 없으면 데이터 베이스 조회 -> 없으면 새로운 엔티티 생성해서 병합
  • 병합은 사실 준영속, 비영속 신경X
  • 식별자 값으로 엔티티 조회할 수 있으면 불러서 병합
  • 없으면 새로 생성해서 병합
  • 즉 save, update 기능을 수행