(Hibernate) 영속성 컨텍스트란?
1. 엔티티의 생명주기
비영속 : 영속성 컨텍스트와 전혀 관계가 없는 상태
영속 : 영속성 컨텍스트에 저장된 상태
준영속 : 영속성 컨텍스트에 저장되었다가 분리된 상태
삭제 : 삭제된 상태
* 비영속
엔티티 객체를 생성했다. 순수한 객체 상태이며 아직 저장하지 않았다.
따라서 영속성 컨텍스트나 데이터베이스와는 전혀 관련이 없다. 이것을 비영속 상태라한다.
예)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
* 영속
엔티티매니저를 통해 엔티티를 영속성 컨텍스트에 저장했다. 이렇게 영속성 컨텍스트가 관리하는 엔티티를 영속 상태라 한다.
이제 회원엔티티는 비영속상태에서 영속상태가 되었다.
결국 영속 상태라는 것은 영속성 컨텍스트에 의해 관리된다는 뜻이다.
그리고 em.find()나 JPQL 을 사용해서 조회한 엔티티도 영속성 컨텍스트가 관리하는 영속상태이다.
em.persist(member);
* 준영속
영속 컨텍스트가 관리하던 영속 상태의 엔티티를 영속성 컨텍스트가 관리하지 않으면 준영속 상태가 된다.
em.detach()나 em.close()나 em.clear()를 호출하면된다.
em.detach(member);
em.remove(member);
<영속성 컨텍스트의 특징>
1. 영속성 컨텍스트와 식별자 값
엔티티를 식별자 값(@Id로 테이블의 기본키와 매핑한 값) 으로 구분한다.
따라서 영속상태는 식별자 값이 반드시 있어야 한다. 없으면 예외가 발생한다.
2. 영속성 컨텍스트와 데이터베이스 저장
영속성 컨텍스트에 엔티티를 저장하면 이 엔티티는 언제 데이터베이스에 저장될까?
JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터베이스에 반영하는데 이것을 플러시(flush)라 한다.
* 장점
- 1차 캐시
- 동일성 보장
- 트랜잭션을 지원하는 쓰기 지연
- 변경 감지
- 지연 로딩
<엔티티 조회>
영속성 컨텍스트는 내부에 캐시를 가지고 있는데 이것을 1차캐시라 한다.
영속상태의 엔티티는 모두 이곳에 저장된다.
쉽게 이야기하면 영속성 컨텍스트 내부에 Map이 하나 있는데 키는 @Id로 매핑한 식별자고 값은 엔티티 인스턴스이다.
//엔티티를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//엔티티를 영속
em.persist(member);
1차 캐시의 키는 식별자 값이다. 그리고 식별자 값은 데이터베이스 기본 키와 매핑 되어 있다. 따라서 영속성 컨텍스트에 데이터를 저장하고 조회하느 모든 기준은 데이터베이스 기본 키 값이다.
Member member = em.find(Member.class, "member1");
em.find를 호출하면 먼저 1차 캐시에서 엔티티를 찾고 만약 찾는 엔티티가 1차 캐시에 없으면 데이터베이스에서 조회한다.
* 1차 캐시에서 조회
em.find()를 호출하면 우선 1차 캐시에서 식별자 값으로 엔티티를 찾는다.
만약 찾는 엔티티가 없으면 데이터베이스를 조회하지 않고 메모리에있는 1차 캐시에서 엔티티를 조회한다.
* 데이터베이스에서 조회
만약 em.find()를 호출했는데 엔티티가 1차 캐시에 없으면 엔티티 매니저는 데이터베이스를 조회해서 엔티티를 생성한다. 그리고 1차 캐시에 저장한 후에 영속 상태의 엔티티를 반환한다.
* JPA는 1차 캐시를 통해 반복 가능한 읽기 (REPEATABLE READ)등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다는 장점이 있다.
Member member = em.find(Member.class, "member1");
em.find를 호출하면 먼저 1차 캐시에서 엔티티를 찾고 만약 찾는 엔티티가 1차 캐시에 없으면 데이터베이스에서 조회한다.
* 1차 캐시에서 조회
em.find()를 호출하면 우선 1차 캐시에서 식별자 값으로 엔티티를 찾는다.
만약 찾는 엔티티가 없으면 데이터베이스를 조회하지 않고 메모리에있는 1차 캐시에서 엔티티를 조회한다.
* 데이터베이스에서 조회
만약 em.find()를 호출했는데 엔티티가 1차 캐시에 없으면 엔티티 매니저는 데이터베이스를 조회해서 엔티티를 생성한다. 그리고 1차 캐시에 저장한 후에 영속 상태의 엔티티를 반환한다.
* JPA는 1차 캐시를 통해 반복 가능한 읽기 (REPEATABLE READ)등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다는 장점이 있다.
* 변경 감지
JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해두는데 이것을 스냅샷이라 한다. 그리고 플러시 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다.
변경감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용된다.
* JPA의 기본전략은 엔티티의 모든 필드를 업데이트한다.
-> 모든 필드를 사용하며 데이터베이스에 보내는 데이터 전송량이 증가하는 단점이 있지만, 다음과 같은 장점으로 인해 모든 필드를 업데이트 한다.
- 모든 필드를 사용하면 수정 쿼리가 항상 같다(물론 바인딩 데이터는 다르다.) 따라서 애플리케이션 로딩 시점에 수정 쿼리를 미리 생성해두고 재사용할 수 있다.
- 데이터베이스에 동일한 쿼리를 보내면 데이터베이스는 이전에 한 번 파싱된 쿼리를 재사용할 수 있다.
댓글
댓글 쓰기