티스토리 뷰
영속성 컨텍스트
- 영속성 컨텍스트: 엔티티를 영구 저장하는 환경
- 눈에 보이지 않는 논리적인 개념
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근 EntityManager.persist(entity); //엔티티를 DB에 저장하는 것이 아닌 엔티티 컨텍스트에 저장. 엔티티를 영속화한다는 뜻.

엔티티 매니저
- 엔티티 매니저를 통해 영속성 컨텍스트에 접근
- 엔티티 매니저와 영속성 컨텍스트는 1:1 또는 N:1 패밍
- 고객의 요청이 올때마다 EntityManager 생성


엔티티의 생명주기

- 비영속 (new/transient)
- 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- JPA와 전혀 관련이 없는 상태
- 영속 (managed)
- 영속성 컨텍스트에 관리되는 상태
- 영속 상태라고해서 바로 DB에 저장되는 것이 아님. DB는 이후에 커밋해야 저장됨.
- 준영속 (detached)
- 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제(removed)
- 영구저장된 DB에서 삭제된 상태
// 객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member)
//회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
//객체를 삭제한 상태(삭제)
em.remove(member);
영속성 컨텍스트의 이점
1. 엔티티 조회, 1차 캐시
- 엔티티 메니저는 DB트랜잭션 단위로 실행됨.
- 고객의 요청이 들어오고 이후 비즈니스가 종료되면, 영속성 컨텍스트도 종료되고 1차 캐시도 날라감.
- 고객이 10명이면 10개의 1차 캐시 생성.
ex) em.persist(entity)
엔티티를 영속화하면 1차 캐시에 저장됨
(바로 DB에 저장되는 것이 아님)


ex) em.find()
조회 요청을 받으면 먼저 DB가 아닌 1차 캐시에 접근.
1차 캐시에 해당 엔티티가 저장되어있는 경우 DB에 접근할 필요 없이 바로 1차 캐시에서 엔티티를 반환.


1차 캐시에 해당 엔티티가 없을 경우
→ DB에 접근
→ DB에서 엔티티를 반환하여 1차 캐시에 저장
→ 1차 캐시에 저장된 엔티티를 애플리케이션에 반환


2. 영속 엔티티의 동일성 보장
자바 컬렉션에서 주소가 같은 객체를 두 번 조회하면 동일한 객체로 인식하는 것처럼
영속성 컨텍스트에서 하나의 객체를 두 번 조회하면 동일힌 객체로 인식
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); // true
3. 엔티티 등록 - 쓰기 지연
엔티티를 등록하면 우선 1차 캐시에 저장했다가 이후 트랜잭션을 커밋하면 캐시에 저장된 엔티티가 한 번에 DB에 저장됨.
버퍼링 개념이라고 보면 됨.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋



4. 엔티티 수정 - 변경 감지
JPA는 dirty checking 기능이 있음.
영속성 컨텍스트에서 자동으로 UPDATE쿼리문을 생성하기 때문에 애플리케이션에서 엔티티 수정 후 추가로 em.update(member); 코드를 작성할 필요가 없음
Dirty Checking
애플리케이션에서 엔티티 수정 후 transaction.commmit() 코드 실행
영속 컨텍스트에서 다음과 같이 동작
→ DB에 commit하기 전 flush가 자동으로 실행(영속성 컨텍스트의 변경 내용을 데이터베이스에 반영)
→ flush: 1차 캐시에서 엔티티와 스냅샷 비교
→ flush: 데이터가 바뀌었다면 (dirty == 1, 엔티티와 스냅샷이 다르다면)
UPDATE쿼리문을 '쓰기 지연 SQL 저장소' 에 저장
→ flush: '쓰기 지연 SQL 저장소' 의 쿼리문을 DB에 flush
→ DB에 커밋
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
//엔티티의 스냅샷이 1차 캐시에 저장됨
//스냅샷: 1차 캐시에 처음으로 저장된 시점의 엔티티
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//em.update(member) 이런 코드가 있어야 하지 않을까? 필요없음
transaction.commit(); // [트랜잭션] 커밋

5. 지연 로딩
플러시
- 플러시
- 영속성 컨텍스트를 비우는 것이 아님
- 영속성 컨테스트의 변경내용을 DB에 동기화 하는 것
- 트랜잭션이라는 작업 단위 안에서 커밋 직전에만 동기화 하면 됨
- em.flush() 실행
→ 1차 캐시에서 엔티티와 스냅샷을 비교해 엔티티 변경 감지
→ 수정된 엔티티를 쓰기 지연 SQL저장소에 등록
→ 쓰기 지연 SQL저장소의 쿼리를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)
- 플러시가 실행되는 경우
- em.flush() - 직접 호출
- 트랜잭션 커밋 - 플러시 자동 호출
- JPQL 쿼리 실행 - 플러시 자동 호출
- 플러시 모드 옵션
- em.setFlushMode(FlushModeType.AUTO) : 커밋이나 쿼리를 실행할 때 플러시 (기본)
- em.setFlushMode(FlushModeType.COMMIT) : 커밋할 때만 플러시
준영속 상태
- 영속 상태: em.persist(), em.find() 에 의해 엔티티가 영속성 컨텍스트의 1차 캐시에 올라온 상태
- 준영속 상태: 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached) 된 상태 영속성 컨텍스트가 제공하는 기능을 사용 못함
- 준영속 상태로 만드는 방법
- em.detach(entity) 특정 엔티티만 준영속 상태로 전환
- em.clear() 영속성 컨텍스트를 완전히 초기화
- em.close() 영속성 컨텍스트를 종료
'백엔드 > 스프링' 카테고리의 다른 글
| JPA - (1) JPA 소개 (1) | 2024.04.18 |
|---|---|
| JPA - (0) sql 중심적인 개발의 문제점 (1) | 2024.04.18 |
| 스프링부트 환경설정 (0) | 2024.03.26 |
| 애플리케이션 제어 (0) | 2023.08.17 |
