영속성 컨텍스트
JPA에서 가장 중요한 2가지
- 객체와 관계형 데이터베이스 매핑하기 (Object Relational Mapping)
- DB를 어떻게 설계하고, 객체를 어떻게 설계해서 중간에서 JPA를 어떻게 사용해서 mapping 할 것 인지
- 영속성 컨텍스트
- 실제 JPA가 내부에서 어떻게 동작하는지
- 영속성 컨텍스트를 이해하면 JPA가 내부적으로 어떻게 돌아가는지 이해할 수 있을 것
Entity Manager Factory와 Entity Manager
예를들어 웹 어플리케이션을 개발할 때 사용자의 요청에 따라서 EMF를 통해 EM을 생성합니다. EM은 내부적으로 DB 커넥션을 통해 DB를 사용합니다.
그렇다면 영속성 컨텍스트는 무엇일까??
영속성 컨텍스트
- JPA를 이해하는데 가장 중요한 용어
- "엔티티를 영구 저장하는 환경"이라는 뜻
- EntityManager.persist(entity);
- 해당 코드의 의미는 DB에 저장되지만 DB에 저장한다는 의미가 아닌 영속성 컨텍스트에 저장한다는 의미입니다.
(아직 어떤 느낌인지 모르겠습니다... ?!?!?!?)=> 실제 확인해보니 em.persist할 때 쿼리문이 나가지 않고 쓰기 지연 SQL 저장소에 쿼리가 저장되고 commit이나 flush를 할 경우 DB에 쿼리문을 보내어 DB를 동기화 시켜줌
- 해당 코드의 의미는 DB에 저장되지만 DB에 저장한다는 의미가 아닌 영속성 컨텍스트에 저장한다는 의미입니다.
EM? 영속성 컨텍스트?
- 영속성 컨텍스트는 논리적인 개념
- 눈에 보이지 않기때문에 이해하기 쉽지 않다.
- EM을 통해 영속성 컨텍스트에 접근
- EM과 영속성 컨텍스트는 J2SE 환경에서 1:1로 매핑됩니다.
- 스프링 프레임워크같은 환경에선 N:1로 매핑되는데 해당 부분은 다음에 이해하는 것을 권장
Entity의 생명주기
- 비영속 (new/transient)
- 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- 영속 (managed)
- 영속성 컨텍스트에 관리되는 상태
- 준영속 (detached)
- 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제 (removed)
- 삭제된 상태
비영속
객체만 생성하고 em에 persist하지 않은 상태
영속
객체를 EM에 저장한 상태 (트랜잭션에 commit하지 않았기때문에 DB에는 올라가지 않는다.)
준영속, 삭제
영속성 컨텍스트의 이점
- 1차 캐시
- 동일성(Identity) 보장
- 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
- 변경 감지(Dirty Checking)
- 지연 로딩(Lazy Loading)
애플리케이션과 DB 사이에 중간 계층이 있음으로서 위와같은 이점을 얻을 수 있다. 영속성 컨텍스트 뿐만 아니라 중간에 하나를 더 두면서 얻는 이점은 버퍼링, 캐시 등과 같은 이점을 얻을 수 있다.
엔티티 조회, 1차 캐시
이렇게 @Id로 member1이 있고, Entity로 객체가 있음으로서 얻는 이점은 1차 캐시에서 조회할 때 있습니다.
1차 캐시에서 조회
em.persist로 1차 캐시에 저장이 되었다고 가정한뒤, 조회한다고 합시다. 조회를 할 경우 DB를 먼저 조회하는 것이 아닌 1차 캐시에서 먼저 조회를 하게 됩니다. 그때 1차 캐시에서 원하는 객체가 있는 경우 DB를 조회하지 않고 바로 객체를 반환 받을 수 있습니다.
데이터베이스에서 조회
조회할 때, 1차 캐시에 없는 경우 DB를 조회하게 됩니다. 이때, DB에 원하는 객체가 있는 경우 1차 캐시에 저장한 뒤, 1차 캐시에 있는 객체를 반환하게 됩니다.
EM을 사용함으로서 위와같은 1차캐시의 이점을 얻을 수 있지만, 현업자분의 말로는 큰 이점은 없다고 합니다. 이유는 EM은 DB 트랜잭션 단위로 만들고 트랜잭션 종료시 종료됩니다. 이 말은 고객의 요청에 따라 비즈니스가 끝나게 될 경우 영속성 컨텍스트를 지운다는 뜻 입니다. 이렇듯 굉장히 찰나에 순간에만 이점을 얻을 수 있기 때문에 사실상 큰 이점은 없다고 합니다.
영속 엔티티의 동일성 보장
영속성 컨텍스트에서 같은 객체를 불러와 비교해보면 자바 컬렉션에서 똑같은 레퍼런스 두개를 가져와 비교할 때 같다고 나오는 것 처럼 같다고 나옵니다.
엔티티 등록
트랜잭션을 지원하는 쓰기 지연
트랜잭션 commit을 하기전까진 1차 캐시에 변경된 내용은 쓰기 지연 SQL 저장소에 쿼리문이 차곡차곡 쌓이게 됩니다. Queue와 비슷한 형식인 것 같습니다. 후에 트랜잭션 commit을 한 경우 쌓인 쿼리문은 flush가 되며 DB에 저장하게 됩니다.
엔티티 수정
변경 감지
데이터를 수정할 때 객체를 가져와 수정 후 update나 persist를 다시 해줘야하는 것 아닌가라는 의문이 생길 수 있습니다. (저도 그랬습니다) 하지만, JPA를 사용하는 목적은 자바에서 컬렉션을 사용하는 것처럼 사용하기 위함 입니다. 컬렉션을 수정할 때, 수정 후 다시 데이터를 집어넣는 작업을 해야하는가를 생각해보면 그렇지 않습니다. 그렇기때문에 JPA에서도 데이터를 수정 후 집어넣는 작업을 하지 않아도 됩니다. 이것이 가능한 이유는 JPA에서 Dirty Checking을 하기 때문입니다.
변경 감지 (Dirty Checking)
JPA는 DB 트랜잭션 커밋하는 시점에 내부적으로 flush()가 호출됩니다. 후 엔티티와 스냅샷을 비교합니다. 1차 캐시 안에는 사실 PK값, 객체 뿐만아니라 스냅샷도 있습니다. 스냅샷은 값을 읽는 최초 시점을 저장한 객체입니다. 그렇기 때문에 flush()가 호출 되어 엔티티와 스냅샷을 비교할 때 최초 시점에 객체와 commit된 객체의 값을 비교 후 변경된 데이터에 대한 update 쿼리문을 자동으로 생성하여 쓰기지연 SQL 저장소에 생성하여 저장합니다. 이러한 내부적인 로직때문에 수정할 때 persist나 update를 안해줘도 됩니다.
엔티티 삭제
위의 메커니즘과 같이 remove(객체)를 하여 삭제할 수 있습니다.
플러시
위 내용을 보다보면 flush라는 단어를 보았을 것 입니다.
flush
영속성 컨텍스트의 변경 내용을 데이터베이스에 반영
플러시 발생
- 변경 감지 (Dirty Checking)
- 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리)
영속성 컨텍스트를 플러시하는 방법
- 직접 호출
- em.flush()
- 플러시 자동 호출
- 트랜잭션 커밋
- JPQL 쿼리 실행
플러시 정리
- 영속성 컨텍스트를 비우지 않음
- 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화
- 트랜잭션이라는 작업 단위가 중요 -> 커밋 직전에만 동기화하면 됨
준영속 상태
준영속 상태
- 영속 -> 준영속
- 영속 상태의 엔티티가 영속성 컨텍스트에서 분리 (detached)
- 영속성 컨텍스트가 제공하는 기능을 사용 못함
준영속 상태로 만드는 방법
- em.detach(entity)
- 특정 엔티티만 준영속 상태로 전환
- em.clear()
- 영속성 컨텍스트를 완전히 초기화
- em.close()
- 영속성 컨텍스트를 종료
출처
'spring > 인프런 강의 정리' 카테고리의 다른 글
[JPA 기본편] 5. 연관관계 매핑 기초 (0) | 2022.03.08 |
---|---|
[JPA 기본편] 4. 엔티티 매핑 (0) | 2022.03.05 |
[JPA 기본편] 2. JPA 시작하기 (0) | 2022.02.21 |
[JPA 기본편] 1. JPA 소개 (0) | 2022.02.19 |
[HTTP 웹 기본 지식] 8. HTTP 헤더 - 캐시와 조건부 요청 (0) | 2022.01.15 |