[데브코스] JPA - 영속성 컨텍스트
Updated: Categories: TILW8D2 Part1 - Entity Manger / 영속성 컨텍스트에 대해 알아보자
Entity Manager
Entity Manager Factory
- Entity Manager를 생성해주는 Bean
- Thread Safe 하다.
Entity Manager
- Entity 객체를 관리(영속성 컨텍스트에 보관)하고 CRUD까지 모두 처리해준다.
- Thread Safe 하지 않음. 즉 여러 스레드가 존재한다면 동시성 이슈가 발생할 수 있다.
- DB에 커밋이 일어나는 시점에만 Entity Manger에서 커넥션을 가져와 사용한다.
- 트랜잭션의 경우 트랜잭션이 시작되는 시점에서 커넥션을 획득함
Persistence Context
- 영속성 컨텍스트는 JPA에서 가장 중요한 요소임
- Entity를 영구 저장하는 환경이라는 뜻
주요 특징
- 영속성 컨텍스트 내의 Entity는 식별자 값이 반드시 필요하다
- 1차 캐시에서 Key-Value 형태로 Entity를 관리하기 때문
- 트랜잭션을 커밋하는 시점에서 영속성 컨텍스트에 새로 저장된 Entity를 DB에 반영(FLUSH)한다
- Flush는 영속성 컨텍스트의 변경내용을 DB에 동기화하는 작업
장점
- 1차 캐시
- 동일성 보장
- 트랙잭션을 지원하는 쓰기 지연
- 변경 감지
- 지연 로딩
Entity Life Cycle
상태
비영속
(new / transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태영속
(managed) : 영속성 컨텍스트에 저장된 상태준영속
(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태삭제
(removed) : 삭제된 상태
동작
- 어떤 Entity 객체가 있다고 했을 때 영속성 컨텍스트의 동작은 아래와 같다
Entity entity = new Entity();
... 객체에 데이터 삽입 ...
// 영속성 컨텍스트에 엔티티 등록
entityManager.persist(entity);
// 영속성 컨텍스트에서 해당 엔티티 분리
entityManager.detache(entity);
// 영속성 컨텍스트 내부 모든 엔티티 분리
entityManager.clear();
// 영속성 컨텍스트 종료
entityManager.close();
// 영속성 컨텍스트에서 해당 엔티티 분리 및 DB에서 삭제
entityManager.remove(entity);
영속성 컨텍스트를 통한 CRUD (예시)
Create
EntityManager em = emf.createEntityManager(); // 1)엔티티 매니저 생성
EntityTransaction transaction = em.getTransaction(); // 2)트랜잭션 획득
transaction.begin(); // 3)트랙잰셕 begin
Customer customer = new Customer(); // 4-1)비영속
customer.setId(1L);
customer.setFirstName("honggu");
customer.setLastName("kang");
em.persist(customer); // 4-2)영속화
transaction.commit(); // 5)트랜잭션 commit
// 트랜잭션이 커밋이 되는 순간 쿼리가 수행된다. flush DB와 동기화가 된다.
- 영속화된 Entity와 관련된 쿼리가 커밋전에는 쓰기 지연 저장소에 잠시 저장된다.
Read (1차 캐시)
... Create 코드와 동일 ...
Customer entity = em.find(Customer.class, 1L); // 1차 캐시에서 조회한다.
log.info("{} {}", entity.getFirstName(), entity.getLastName());
find
시 DB에 Entity에 해당하는 쿼리를 날리지 않고 1차 캐시에서 정보를 가져온다.
Read (DB)
... Create 코드와 동일 ...
em.clear(); //영속성 컨텍스트를 초기화 한다.
Customer entity = em.find(Customer.class, 1L); // DB 에서 조회한다. SELECT ...
log.info("{} {}", entity.getFirstName(), entity.getLastName());
em.find(Customer.class, 1L); // SELECT Query 수행되지 않는다. 1차캐시 사용
- 영속성 컨텍스트를 초기화했기 때문에 1차 캐시에는 아무런 내용이 없음
- 따라서 첫번째
find
시 DB에 직접 쿼리를 날려 조회한다. - 두번째
find
시엔 앞에서 쿼리를 실행했었기 때문에 1차 캐시에 해당 Entity 정보가 담겨있음.
Update
... Create 코드와 동일 ...
transaction.begin();
Customer entity = em.find(Customer.class, 1L);
// entity 객체 수정
entity.setFirstName("guppy");
entity.setLastName("hong");
transaction.commit(); // flush -> UPDATE ...
- 커밋(Flush) 발생
- Entity를 기존 스냅샷(최초의 Entity 상태)과 비교
- 스냅샷 변경감지(
dirty checking
)시 변경 쿼리를 쓰기 지연 저장소에 저장 - 커밋시 저장된 쿼리 실행
Delete
... Create 코드와 동일 ...
transaction.begin();
Customer entity = em.find(Customer.class, 1L);
em.remove(entity);
transaction.commit(); // flush -> DELETE ..
- 영속성 컨텍스트에서 entity 삭제
- 커밋(Flush) 발생시 삭제 감지 후 쿼리 실행