개발/java

[Spring - JPA] 3. 영속성관리

ebang 2024. 2. 18. 19:30
반응형
  • JPA 에서 가장 중요한 2가지
    • 객체와 관계형 데이터 베이스를 어떻게 매핑하나
    • 영속성 컨텍스트 (JPA 가 내부적으로 어떻게 동작하나)

1. 영속성 컨텍스트란?

  • 'Entity를 영구 저장하는 환경’이라는 뜻
  • EntityManager.persist(entity);
  • EntityManager를 통해 영속성 컨텍스트에 접근.

2. Entity 의 생명주기

  • 비영속(new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
  • 영속(managed) : 영속성 컨텍스트에 관리되는 상태 ((em.persist(member)))
  • 준영속(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태 (em.detach(member))
  • 삭제 (removed) : 삭제된 상태

*영속상태가 되었다고 해서 바로 데이터베이스에 쿼리가 생기는 것은 아님.

3. PersistenceContext의 이점

  • 정리 : 1차 캐시 / 동일성 보장 / 트랜잭션을 지원하는 쓰기 지연 / Dirty checking (변경 사항 저장) / 지연 로딩 (Lazy loading)

1. 1차 캐시

  • persist 를 통해 관리하게 되거나, 데이터베이스에서 조회하고 나면 그 결과를 영속성 컨텍스트 내부의 1차 캐시에 저장한다.
  • pk @id 와 객체를 저장한다.
  • 보통 트랜잭션 단위로 entityManager 가 생성되고 사용된 후 삭제되기 때문에, 이런 1차캐시는 한 트랜잭션 내로만 의미가 있고 성능상 이점이 많지는 않다.
    (한 트랜잭션 마다 1차 캐시가 존재. 고객 10명이 연결되었다면 10명 모두 다른 1차 캐시 사용. 이 매커니즘 상의 이점이 있는 것이라고 보면 됨.)

2. 영속성 엔티티의 동일성 보장

- 컬렉션에 저장한 객체의 레퍼런스 비교 처럼 비교 가능.  - 자세히 이야기하면 - 1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공 - 1차 캐시에서 인스턴스를 보관하고 있다가 요청 시 그 인스턴스를 제공하기 때문에 가능한 일. 

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

  • persist 시에는 DB까지 저장하지 않고 1차 캐시에 엔티티 저장, 쓰기 지연 SQL 저장소에 sql문에 저장해두었다가, 트랜잭션을 커밋시에 저장해둔 sql문을 db에 날린다.
  • sql문을 바로 보내지 않는 대신 최적화가 가능하다 : 커넥션 한번에 여러개의 쿼리를 날릴 수 있는. (배치 쿼리)

4. 엔티티 수정 변경 감지

  • 이렇게만 해도, 데이터베이스 내부의 내용이 변경된다.
  • dirty checking
  • 커밋을 하면 내부적으로 flush() 가 호출됨.
  • entity와 스냅샷을 비교.
  • 변경사항이 있다면 update 쿼리문을 날린다.

4. Flush

  • 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는.
  • flush가 호출되면
      1. 1차 캐시 내용과 비교해서 변경을 감지한 경우 updqte 쿼리문을 쓰기 지연 sql 저장소에 저장.
      1. 저장해둔 쓰기 지연 sql 저장소의 sql문을 실행. (등록, 수정, 삭제 쿼리들)
  • flush를 호출하는 방법
      1. em.flush()
      1. 트랜잭션 커밋 (쟈동 flush 호출)
    • JPQL 쿼리 실행 (자동 flush 호출)
      (flush 되었다고 1차 캐시 내용이 사라지거나 하는 것은 아님.
      단지 작업환경의 영속성 컨텍스트의 변경내용과 데이터베이스와 동기화작업이라고 보면 될듯)
      (트랜잭션이라는 작업 단위가 중요: 커밋 직전에만 동기화가 되면 되는 것이다.)

  • 원래는 === 이후에 발생했던 쿼리가 그 이전에 날라가는 것을 볼 수 있다.

(JPQL 쿼리 실행 시 자동 호출되는 이유:
다음과 같은 코드에서 JPQL을 실행할 때 애초에 데이터베이스에 반영된 memberA,. B, C가 없으므로 문제가 될 수 있기 때문에, 기본적으로 쌓아둔 sql 저장소의 쿼리 들을 실행한 후(flush 호출 후) JPQL 을 실행하도록 한다. )

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

5. 준영속 상태

  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태. (정확히 어떤 상태인지 예시는 추후 설명.)
  • 영속성 컨텍스트가 제공하는 기능은 사용못함.
  • 준영속상태가 되는 방법
    • em.detach(entity)
    • 커밋 직전에 select 쿼리만 일어날 뿐, setName으로 인한 변경사항을 담은 update 쿼리는 일어나지 않는다. (detach 를 통해서 영속성 컨텍스트에서 관리하던 1차 캐시에서 제거되었으므로 변경사항을 감지할 수 없음.)
  • em.clear()
    • 영속성 컨텍스트를 완전히 초기화.
    • find 하고 나서 clear() 하고나면, 1차 캐시에서 지워졌기 때문에 그 다음 find에서 다시 select를 해야한다.
    • 총 2번의 select 쿼리가 일어나는.
  • em.close()
    • 영속성 컨텍스트를 종료
반응형