티스토리 뷰

Books

[JPA] 영속성(persistence)이란?

Aaron 2020. 12. 18. 21:47
반응형


| 영속성




|| 영속성 컨텍스트(persistence context)



- 엔티티 매니저로 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리

- 엔티티 매니저를 생성할 때 하나 만들어짐 

  -> 엔티티 매니저를 통해 영속성 컨텍스트에 접근할 수 있고, 영속성 컨텍스트를 관리할 수 있음

- 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 DB에 반영 == .flush()

- 영속 상태의 엔티티는 모두 영속성 컨텍스트의 내부 캐시(1차 캐시)에 저장

- 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스와 같은 역할


- 조회한 엔티티만 영속석 컨텍스트가 관리 (임베디드 타입, 단순 필드는 X)



|| 장점



1차 캐시

동일성 보장

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

변경 감지

지연 로딩



|| 엔티티의 생명주기



출처 https://ultrakain.gitbooks.io/jpa/content/chapter3/chapter3.3.html


> 비영속(new/transient)

영속성 컨텍스트와 전혀 관계가 없는 상태 (순수한 객체 상태)


> 영속(managed)

엔티티 매니저를 통해 엔티티를 영속성 컨텍스트에 저장 (영속성 컨텍스트에 의해 관리)


> 준영속(detached)

영속성 컨텍스트에 저장되었다가 분리된 상태


<엔티티를 준영속 상태로 전환하는 방법>

em.detach(entity) : 특정 엔티티만 준영속 상태로 전환, 영속성 컨텍스트로부터 분리

  - 1차 캐시, 쓰기 지연 SQL 저장소 정보 제거

  - 영속 상태였다가 더는 영속성 컨텍스트가관리하지 않는 상태

  - 쓰기 지연 SQL 저장소의 Insert SQL 도 제거되어 DB에 저장되지 않음

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

  - 영속성 컨텍스트를 초기화해서 해당 영속성 컨텍스트의 모든 엔티티를 준영속 상태로 만듦

  - 영속성 컨텍스트를 제거하고 새로 만든 것과 같음

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

  - 해당 영속성 컨텍스트가 관리하던 영속 상태의 엔티티가 모두 준영속 상태로


<엔티티를 영속 상태로 전환하는 방법>

em.merge(entity), 병합 : 준영속 상태의 엔티티를 다시 영속 상태로 변경, 새로운 영속 상태의 엔티티를 반환

   - 파라미터로 넘어온 엔티티의 식별자 값으로 영속성 컨텍스트를 조회하고 찾는 엔티티가 없으면,

     데이터베이스에서 조회. 

   - 만약 데이터베이스에서도 발견하지 못하면 새로운 엔티티를 생성해서 병합

   - 병합은 준영속, 비영속을 신경쓰지 않고, 식별자 값으로 엔티티를 조회할 수 있으면

     불러서 병합하고 조회할 수 없으면 새로 생성해서 병합,

   - 병합은 save or update 기능 수행


- 삭제 (removed) : 엔티티를 영속성 컨텍스트와 DB에서 삭제



|| 엔티티 조회



ㅇ 1차 캐시

- 영속성 컨테스트 내부 캐시

- Map으로 저장. Key는 @Id, value는 엔티티 인스턴스

- 영속성 컨텍스트에 데이터를 저장하고 조회하는 모든 기준은 데이터베이스 기본 키 값

- em.find() 를 호출해서 먼저 1차 캐시에서 엔티티를 찾고

  만약 찾는 엔티티가 1차 캐시에 없으면 데이터베이스에서 조회하여 엔티티를 생성(성능상 이점)

ㄴ 단, JPQL은 항상 먼저 데이터베이스에 SQL을 실행해서 결과를 조회

ㄴ 이때, 영속성 컨텍스트에 이미 조회한 같은 엔티티가 있을 경우,

   새로 검색한 엔티티는 버리고 영속성 컨텍스트에 있는 기존 엔티티를 반환

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

  em.find()로 반복해서 호출해도 영속성 컨텍스트는 1차 캐시에 있는 같은 엔티티 인스턴스를 반환

- 영속성 컨텍스트는 성능상 이점과 엔티티의 동일성(인스턴스가 같음)을 보장

   ㄴ 영속성 컨텍스트가 같으면 동일한 엔티티 반환



|| 엔티티 등록



ㅇ 쓰기 지연

- 엔티티 매니저는 트랜잭션을 커밋하기 직전까지 데이터베이스에 엔티티를 저장하지 않고

  내부 쿼리 저장소에 Insert SQL 을 모아둠 => 트랜젝션을 커밋할 때 모아둔 쿼리를 DB에 보냄

- 이 기능을 잘 활용하면 모아둔 등록 쿼리를 DB에 한 번에 전달하여 성능 최적화가 가능



|| 엔티티 수정



- JPA로 엔티티를 수정할 때는 단순히 엔티티를 조회해서 데이터만 변경하면 됨

- 변경 감지(dirty checking) : 엔티티의 변경사항을 데이터베이스에 자동으로 반영

- JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해둔다(스냅샷)

- 플러시 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾음

- 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용

- 비영속, 준영속처럼 영속성 컨텍스트의 관리를 받지 못하는 엔티티는 값을 변경해도 DB에 반영되지 않음


- 동작

1. 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 flush() 호출

2. 엔티티와 스냅샷을 비교해서 변경된 엔티티 찾기

3. 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보냄

4. 쓰기 지연 저장소의 SQL을 DB에 보냄

5. DB 트랜젝션을 커밋


- 엔티티 수정 시 모든 필드를 사용

- 수정된 데이터만 사용해서 동적으로 update sql 생성 시 DynamicUpdate Annotation 사용



|| 엔티티 삭제



- 엔티티 등록과 비슷하게 삭제 쿼리를 쓰기 지연 SQL 저장소에 등록





| Flush



|| Flush()



- 플러시는 영속성 컨텍스트의 변경 내용을 DB에 반영

1. 변경 감지가 동작해서 영속성 컨텍스트에 있는 모든 엔티티를 스냅샷과 비교해서 수정된 엔티티를 찾음.

   수정된 엔티티는 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록

2. 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송(등록, 수정, 삭제 쿼리)


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

1. em.flush() 호출

- 테스트나 다른 프레임워크와 JPA를 함께 사용할 경우를 제외하고 거의 사용 X

2. 트랜잭션 커밋 시 플러지 자동 호출

- 영속성 컨텍스트이 변경 내용을 DB에 반영하기 위함

3. JPQL 쿼리 실행 시 플러시 자동 호출

- 쿼리를 실행하기 직전에 영속성 컨텍스트를 플러시해서 변경 내용을 DB에 반영하기 위함

- Find() 메소드를 호출할 떄는 플러시가 실행되지 않음


* JPA를 사용하지 않고 JDBC를 직접 사용하여 SQL을 실행할 경우,

- JDBC로 쿼리 실행 전 em.flush()를 호출해서 영속성 컨텍스트의 내용을 데이터베이스에 동기화하는 것이 안전

- JPA는 JDBC가 실행한 쿼리를 인식할 방법이 없음



||| JPQL and FlushMode



* 보통 플러시 모드에 따라 커밋하기 직전이나 쿼리 실행 직전에 자동으로 플러시가 호출

> em.setFlushMode(FlushModeType.AUTO);      // 커밋 또는 쿼리 실행 시 플러시(Default)

> em.setFlushMode(FlushModeType.COMMIT);    // 커밋시에만 플러시(성능 최적화를 위해 꼭 필요할 경우만 사용)

 

 

||| Query and FlushMode



- JPQL은 영속성 컨텍스트에 있는 데이터를 고려하지 않고 데이터베이스에서 데이터를 조회

- (의도치 않은 결과를 피하기 위해) JPQL을 실행하기 전 영속성 컨텍스트의 내용을 데이터베이스에 반영해야 함


em.setFlushMode(FlushModeType.COMMIT) 로 설정되었을 경우, 아래와 같은 조치가 필요

1. em.flush()를 직접 호출해주거나

2. .setFlushMode(FlushModeType.AUTO) 로 쿼리에서 플러시 모드 설정



||| FlushMode and Optimization



em.setFlushMode(FlushModeType.COMMIT) 모드로 설정하는 이유.


- 트랜잭션을 커밋할 때만 플러시

-> JPA 쿼리 사용 시 영속성 컨텍스트에는 있지만 아직 데이터베이스에 반영하지 않은 데이터를 조회할 수 없음

  (무결성에 심각한 피해를 줄 수 있음)

-> But, 플러시가 너무 자주 일어나는 상황에 COMMIT 모드를 사용하면, 

   쿼리시 발생하는 플러시 횟수를 줄여 성능을 최적화할 수 있음.





출처 : 자바 ORM 표준 JPA 프로그래밍

반응형
댓글
최근에 올라온 글
최근에 달린 댓글
링크
Total
Today
Yesterday