쌓고 쌓다

[JPA] 영속성 컨텍스트, 엔티티의 생명주기 본문

프로그래밍/JPA

[JPA] 영속성 컨텍스트, 엔티티의 생명주기

승민아 2023. 7. 6. 22:20

Entity ManagerFactory, Entity Manager

1. 엔티티 매니저 팩토리의 생성 비용은 매우 크므로 하나만 만들어 애플리케이션 전체에서 공유한다.

2. 엔티티 매니저 팩토리는 여러 스레드가 동시에 접근해도 안전하며 다른 스레드간에 공유가 가능하다,

3. 엔티티 매니저는 동시에 접근하면 동시성 문제가 발생하므로 스레드가 공유가 불가능하다.

4. 엔티티 매니저는 DB 연결이 꼭 필요한 시점까지 커넥션을 얻지 않는다. 보통 트랜잭션 시작할때 획득한다.

 

영속성 컨텍스트

'엔티티를 영구 저장하는 환경'이라는 뜻이다.

여러 엔티티 매니저가 같은 영속성 컨텍스트에 접근할 수 있지만 현재는 하나에 하나의 영속성 컨텍스트가 생긴다고 알자.

 

엔티티의 생명주기

 1. 비영속: 영속성 컨텍스트와 전혀 관계가 없는 상태

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

 3. 준영속: 영속성 컨텍스트에 저장되었다 분리된 상태

 4. 삭제: 삭제된 상태

 

비영속

엔티티 객체를 생성했을뿐 아직 순수학 객체 상태이며 저장하지 않은 상태.

Member member = new Member();
member.setId("member1");

 

영속

엔티티 매니저를 통해서 영속성 컨텍스트에 저장한 상태.

영속성 컨텍스트가 관리하는 엔티티를 영속 상태라고 한다.

em.find(), JPQL을 사용해서 조회한 엔티티도 영속 상태가 된다.

em.persist(membr);

em.find(Mmeber.class, "memberA")

 

준영속

영속 상태의 엔티티가 영속성 컨텍스트가 관리하지 않으면 준영속 상태가 된다.

em.detach(), em.close() 또는 em.clear()를 통해 영속성 컨텍스트를 초기화해도 준영속 상태가 된다.

em.detach(member); // 회원 엔티티를 영속성 컨텍스트에서 분리

 

 

영속성 컨텍스트의 특징

  • 영속성 컨텍스트는 엔티티를 식별자 값(@Id)로 구분하므로 영속 상태는 식별자 값이 반드시 있어야 함.
  • 영속성 컨텍스트에 저장되었다고 바로 DB에 저장하는 것이 아니라 트랜잭션 커밋 순간 플러쉬(flush)를 통해 반영 함.

 

영속성 컨텍스트 장점

  • 1차 캐시
  • 동일성 보장
  • 쓰기 지연
  • 변경 감지
  • 지연 로딩

1차 캐시

영속성 컨텍스트 내부에 캐시를 갖는다. 이 캐시를 1차 캐시라고 함.

쉽게, 키 @Id와 값 엔티티 인스턴스 Map이 영속성 컨텍스트에 있다고 생각하자.

저장, 조회 모두 DB 기본 키와 매핑하여 찾는다.

 

em.persist(member);

persist를 하더라도 1차 캐시에 저장하지 바로 DB에 저장되진 않는다.

 

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

우선 1차 캐시에서 식별자 값으로 찾고 없으면 DB를 조회해서 엔티티를 생성한다.

그리고 영속성 엔티티를 반환한다. 찾은 데이터는 1차 캐시에 저장한다.

=> 성능상 이점 획득

 

동일성 보장

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

System.out.println(a == b); // 참

1차 캐시의 같은 엔티티 인스턴스를 반환하므로, 영속선 컨텍스트는 엔티티의 동일성을 보장한다.

 

쓰기 지연 (엔티티 등록 과정)

em.persist(member)를 한다고 바로 INSERT SQL을 DB에 보내지 않는다.

트랜잭션 커밋 직전까지 1차 캐시에 반영(저장)하며 내부 쿼리 저장소에 SQL을 차곡차곡 모았다가

트랜잭션 커밋할 때 flush하여 모아둔 쿼리를 DB에 보낸다.

 

변경 감지(dirty checking)

em.find()하여 수정한 후 em.update()를 해야할 것 같지만 아니다.

1. JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 저장(스냅샷)해둔다.

2. 플러시 시점에 스냅샷과 엔티티를 비교하여 변경된 엔티티를 찾아 쓰기 지연 SQL 저장

3. 쓰기 지연 저장소 SQL을 DB에 반영

4. 트랜잭션을 커밋

=> 변경 감지는 영속 상태의 엔티티에만 적용된다.

 

지연 로딩(Lazy Loading)

실제 객체 대신 프록시 객체를 로딩해두고 해당 객체를 실제 사용할 때 영속성 컨텍스트를 통해 불러오는 방법.

 

엔티티 삭제

삭제하려면 엔티티를 먼저 조회해야 한다.

물론 엔티티를 즉시 삭제하지 않고 삭제 쿼리를 쓰기 지연 SQL 저장소에 등록하여

트랜잭션을 커밋해서 플러시를 호출하면 삭제 쿼리를 DB에 전달한다.

Member member = em.find(Member.class, "memberA");
em.remove(member);
Comments