영속성 컨텍스트?

Entity를 영구 저장하는 환경

어플리케이션과 DB 사이에서 객체를 보관하는 가상의 DB같은 역할

서비스별로 EntityManager Factory 존재

Entity Manager Factory에서 DB접근 트랜잭션 발생시마다 스렏르별로 EntityManager를 생성하여 영속성 컨텍스트에 접근

EntityManager를 통해 Entity저장, 조회 시 영속성 컨텍스트에 Entity 보관하고 관리

영속성 컨텍스트는 EntityManager를 생성할 때 생성

 

em.persist(entity);

 

** Spring에서는 EntityManager를 주입하여 사용하면 같은 트랜잭션의 범위에 있는 EntityManager는 같은 영속성 컨텍스트에 접근 **

 

 

EntityManager

영속성 컨텍스트 내에서 Entity를 관리

JPA에서 제공하는 interface로 Spring bean으로 등록되어 @AutoWired로 사용 가능

@Autowired
private EntityManager em;

 

 

 

Entity 생명주기

 

new / trasient

비영속

영속성 컨텍스트와 관계 없는 상태

Entity 객체를 생성했지만, 아직 영속성 컨텍스트에 저장하지 않은 상태

User user = new User();

 

 

managed

영속

영속성 컨텍스트에 저장된 상태

Entity가 영속성 컨텍스트에 의해 관리됨

영속 상태라고 바로 DB에 값이 저장되지 않고 트랜잭션 커밋 시점에 영속성 컨텍스트에 있는 정보를 DB에 쿼리로 전달

@Autowired
private EntityManager entityManager;

    User user = new User();
    entityManager.persist(user);

 

 

detached

준영속

영속성 컨텍스트에 저장되었다가 분리된 상태

Entity를 준영속 상태로 만들기 위해서는 entityManager.detach() 호출

// managed to detached
entityManager.detach(user);

// 영속성 컨텍스트 비우기
// 영속성 컨텍스트를 비우면 관리되고 있던 Entity들은 준영속 상태로 변경
// 대기 상태에 있던 변경 데이터도 삭제
entityManager.clear();

// 영속성 컨텍스트 종료
// 커밋 이전에 영속성 컨텍스트를 종료하면 관리되던 Entity들은 준영속 상태로 변경
entityManager.close();

// detached to managed
// detached 상태의 Entity를 merge하면 다시 영속상태로 전환
entityManager.merge(user);

- 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩을 포함한 영속성 컨텍스트가 제공하는 어떤 기능도 동작 X

- 식별자 값 존재

 

 

removed

삭제

영속성 컨텍스트와 DB에서 해당 Entity를 삭제한 상태

entityManager.remove(user);

 

 

 

영속성 컨텍스트의 특징

Entity를 식별자 값 (@Id로 테이블 PK와 매핑한 값)으로 구분

영속 상태는 식별자 값이 반드시 존재

식별자 값 없을 시 예외 발생

 

JPA는 보통 트랜잭션 커밋하는 순간 영속성 컨텍스트에 새로 저장된 Entity를 DB에 반영

flush

 

1차 캐시

영속성 컨텍스트는 내부에 캐시를 가지고 있음

영속 상태의 Entity는 모두 이곳에 저장

 

** 영속성 컨텍스트 내부의 Map 존재

Key : @Id 매핑한 값

Value : Entity 인스턴스

 

조회 과정

Member member = em.find(Member.class, "member1");

1. 1차 캐시에서 Entity검색

2. 존재하면? >> 메모리에 있는 1차 캐시에서 Entity 조회

3. 존재하지 않으면? >> DB에서 조회

4. 조회한 데이터로 Entity 생성해 1차 캐시에 저장 (Entity를 영속 상태로)

5. 조회한 Entity 리턴

 

 

** 1차 캐시에서 조회

 

 

** DB에서 조회

 

 

 

동일성 보장

영속성 컨텍스트는 Entity의 동일성을 보장

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.print(a==b) // true

동일성 비교 : 실제 인스턴스가 같다. ==을 사용해 비교한다.
동등성 비교 : 실제 인스턴스는 다를 수 있지만 인스턴스가 가지고 있는 값이 같다. equals()메소드를 구현해서 비교한다.

 

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();

// 엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야 한다.
transaction.begin();    // 트랜잭션 시작

em.persist(memberA);
em.persist(memberB);
// 여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.

// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit();   // 트랜잭션 커밋

 

Transactional Write-Behind (트랜잭션을 지원하는 쓰기 지연)

EntityManager는 트랜잭션을 커밋하기 직전까지 내부 쿼리 저장소에 SQL 임시 저장

트랜잭션 커밋할 때 모아둔 쿼리를 DB로 전송

이것을 "트랜잭션 지원하는 쓰기 지연" 이라고 함

 

쓰기 지연, 회원 A 영속

 

 

쓰기 지연, 회원 B 영속

 

 

트랜잭션 커밋, flush, 동기화

 

 

1. 트랜잭션 커밋

2. EntityManager >> 영속성 컨텍스트 flush

3. DB 동기화 >> 등록, 수정, 삭제한 Entity를 DB 반영

** 쓰기 지연 SQL저장소에 모인 쿼리를 DB로 전달

4. 실제 DB 트랜잭션 커밋

 

 

 

변경 감지

 

1. 트랜잭션 커밋 시, EntityManager 내부에서 먼저 flush 호출

2. Entity와 스냅샷 비교하여 변경된 Entity탐색

3. 변경된 Entity가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL저장소에 저장

4. 쓰기 지연 저장소의 SQL flush

5. 실제 DB에 트랜잭션 커밋

 

 

 

 

업데이트 기본 전략

JPA의 기본 전략은 모든 필드를 업데이트

- 수정쿼리가 같고

- 동일 쿼리 보내면 DB는 이전에 파싱된 쿼리를 재사용 할 수 있기 때문

 

** 필드가 너무 많거나 저장되는 내용이 크면??

Hiberante 확장 기능 사용

@Entity
@org.hibernate.annotation.DynamicUpdate
@Table(name = "Member")
public class Member {...}

수정된 데이터만 사용해서 동적으로 udate SQL 생성

 

 

엔티티 삭제

Entity 즉시 삭제 X

삭제 쿼리를 쓰기 지연 SQL저장소에 저장

em.remove() 호출 시, 영속성 컨텍스트에서 제거

 

 

플러시

영속성 컨텍스트의 변경 내용을 DB에 반영

영속성 컨텍스트의 Entity를 지우는게 아니라, 변경 내용을 DB에 동기화 하는것

 

1. 변경 감지 동작, 스냅샷과 비교해서 수정된 Entity 탐색

2. 수정된 Entity에 대해 수정 쿼리를 만들고 쓰기 지연 SQL 저장소에 저장

3. 쓰기 지연 SQL저장소의 쿼리를 DB에 전달

 

flush 방법

- em.flush() 호출

거의 사용 X

테스트, JPA와 타 프레임워크 중복 사용할 때 주로 사용

 

- 트랜잭션 커밋 시 자동 호출

 

- JPQL 쿼리 실행 시 자동 호출

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

// 중간에 조회
query = em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();

Entity가 DB에 반영되지 않고 영속성 컨텍스트에만 존재할 때 조회 실행 하는 경우가 있으므로

JPQL 실행 시에 flush를 자동 호출하여 영속성 컨텍스트의 데이터를 DB에 반영한 후, 조회 실행

 

'끄적 > BE' 카테고리의 다른 글

Inversion of Control (제어 반전)  (0) 2023.01.03
Dependency Injection (의존성 주입)  (0) 2023.01.03
Spring MVC  (0) 2022.12.29
JPA, Spring Data Jpa  (0) 2022.12.28
JPA  (0) 2022.12.28

+ Recent posts