본문 바로가기

개발/JPA

[JPA 기본] 3. 영속성 관리

영속성 컨텍스트 

JPA 중요 개념 2가지

1. 객체와 관계형 데이터베이스 매핑하기(Object Relational Mapping)

2. 영속성 컨텍스트 -> 이번 포스팅에서 소개 예정 

 

엔티티 매니저 팩토리엔티티 매니저의 동작 원리

(1)클라이언트의 요청이 온다.

(2) 엔티티 매니저 팩토리는 요청별로 엔티티 매니저를 만든다.

(3)각 매니저는 커넥션 풀의 커넥션을 이용하여 DB와 통신한다. 

 

영속성 컨텍스트

- 정의: 엔티티를 영구 저장하는 환경

- 코드

EntityManger.persist(entity)

** 영속성 컨텍스트에 entity를 저장한다. DB에 직접저장하는 것이 아님을 주의하자.

** 트랜젝션에서 commit하는 시점에 DB에 쿼리 전달 된다.

 

엔티티 매니저 & 영속성 컨텍스트

- 엔티티 매니저를 통해 영속성 컨텍스트에 접근한다.

- 엔티티 매니저와 영속성 컨텍스트는 1:1 관계로, 엔티티 매니저 안에 영속성 컨텍스트가 포함된다. 

 

영속성 컨텍스트의 이점

1. 1차 캐시

- 같은 트랜잭션 내에서 동일한 객체를 (저장+조회) 혹은 (조회+조회)할 경우, 캐시에서 정보를 가져올 수 있으므로 빠르다. 

- 비즈니스 로직 종료 이후 EntityManagerr를 버리면서 1차 캐시도 같이 날아가므로, 사실상 많이 쓰이지 않는다. 

- 애플리케이션 전체에서 사용하는 캐시(=2차캐시) 아님!

 

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

- 1차 캐시를 사용하여, 반복가능한 읽기(Repeatable Read)를 애플리케이션 차원에서 제공. 

 

* 반복 가능한 읽기(Repeatable Read)란?

하나의 트랜잭션에서 동일한 데이터를 읽을 경우, 데이터의 결과 값은 항상 같다. 

The REPEATABLE READ allows you to read the same data repeatedly and it makes sure that any transaction cannot update this data until you complete your reading. If you are selecting the same row twice in a transaction, you will get the same results both the times

 

3. 엔티티 등록: 트랜잭션을 지원하는  쓰기 지연 (= transactional write-behind)

commit 직전까지 요청 내용들을 모은후, 한번에 여러 쿼리를 보낸다.

 

4. 엔티티 수정/삭제: 변경 감지 (Dirty checking)

- em.update(member)과 같은 코드 필요 없다.

- 엔티티 조회 후 업데이트만 하면 됨.

**변경 감지 작동원리

처음 조회시, 엔티티의 스냅샷을 1차 캐시에 저장해둔다.

commit 시점에서 Entity와 스냅샷을 비교하여, 변화가 있을 경우, 업데이트 쿼리를 쓰기 지연 SQL 저장소에 넣어둔다. 

해당 쿼리는 DB에 반영되어 업데이트가 이뤄진다.

 

5. 지연 로딩 (Lazy Loading)

실무에서 중요한 개념. 다른 포스팅에서 깊게 다룰 예정이다.

 

주의

JPA에서 entity는 반드시 파라미터가 없는 (= no args) public 혹은 protected 생성자가 있어야만 한다.

 

레퍼런스 

1. 반복 가능한 읽기란?

https://www.dbrnd.com/2016/04/sql-server-what-is-repeatable-read-isolation-level/


플러시

플러시란?

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

 

플러시 발생시 무슨 일이 일어날까

1. 변경 감지 (=Dirty checking)

2. 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록한다

3. 쓰기 지연 SQL 저장소 내부의 쿼리(등록/수정/삭제)를 데이터 베이스에 전송한다

 

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

1. em.flush (직접 호출)

** em.flush를 호출시 1차 캐시가 지워지는 것은 아니다.  쓰기 지연 SQL 저장소의 쿼리가 DB에 반영되는 것 뿐임을 주의하자. 

2. 트랜잭션 커밋(자동 호출)

3. JPQL 쿼리 실행 (자동 호출)

** JPQL 쿼리 실행시 플러시가 자동으로 호출되는 이유

아래 예제에서 확인할 수 있듯이, 중간 로직에서 JPQL로 조회가 실행되면, DB에 데이터가 없을 경우 제대로 조회되지 않는다. 따라서 JPQL실행전 플러시가 자동으로 호출된다.

// 저장
em.persist(member)

// 중간에 JPQL실행
query = em.createQuery("select m from Member m", Member.class);

 

플러시 모드 옵션

em.setFlushMode(FlushModeType.COMMIT)

1) FlushModeType.AUTO

커밋이나 쿼리를 실행할 때 플러시(기본값)

2) FlushModeTpye.COMMIT

커밋할 때만 플러시

-> JPQL에서 조회하는 데이터가 앞에서 저장된 데이터와 다를 경우, 플러시가 필요하지 않으므로, 위 옵션을 사용해도 된다. 

하지만, 헷갈리니 AUTO로 유지하길 권장한다.

 

플러시 요약

플러시는 영속성 컨텍스트를 비우지 않는다. 다만 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화할 뿐이다. 이 때, 플러시가 가능한 이유는 트랜잭션이라는 작업 단위가 있기 때문이다. 커밋 직전에만 동기화를 하면 된다. 


준영속 상태

준영속 상태

- 영속상태의 엔티티가 영속성 컨텍스트에서 분리된(=detached) 경우 

- 영속성 컨텍스트가 제공하는 기능 사용 불가

 

준영속 상태로 만드는 방법

1. em.detach(entity) : 특정 엔티티만 준영속 상태로 전환

2. em.clear() : 영속성 컨텍스트 전체 초기화

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

 

** 영속 상태:

- em.persist 로 1차 캐시에 저장된 상태

- em.find 실행시, 1차 캐시에 없어서 DB에서 데이터를 가져와 1차 캐시에 추가한 경우