JPA 영속성 컨텍스트(준영속 상태)

김민석·2023년 10월 10일
0

JPA

목록 보기
4/4
post-thumbnail

Entity의 생명주기

비영속
-영속성 컨텍스트와 전혀 관련없는 새로운 상태
영속
-영속성 컨텍스트에 관리되는 상태
준영속
-영속성 컨텍스트에 저장되었다가 분리된 상태
삭제
-삭제된 상태

영속성 컨텍스트란?

엔티티를 영구 저장하는 환경
영속성 컨텍스트는 애플리케이션과 DB 사이에서 객체를 보관하는 가상의 DB 역할을 한다. 엔티티 매니저(EntityManager)를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리하게 된다.

Entitymanager em;
em.persist(entity); //entity는 영속상태
//persist는 entity를 DB에 저장하는 것이 아니라 영속성 컨텍스트에 저장하는 것

J2SE
환경 엔티티 매니저와 영속성 컨텍스트는 1:1이다.
즉, 엔티티 매니저 생성시 영속성 컨텍스트가 생성된다.

영속성 컨텍스트의 이점

1. 1차 캐시

=> 비즈니스 로직이 굉장히 복잡한 경우에 효과 발휘

em.persist(member)로 저장을 했을 때 1차 캐시에 저장이 되었으므로
1차캐시에 있는 값을 조회 했으므로 select 쿼리 문이 나타나지 않았다.

코드

package hellojpa;

import jakarta.persistence.*;


public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx =em.getTransaction();
        tx.begin();

        try{
            Member member = new Member();
            member.setId(100L);
            member.setName("HelloJPA");

            em.persist(member);

            Member findMember = em.find(Member.class, 100L);
            
            tx.commit();
        } catch (Exception e){
            tx.rollback();
        } finally {
            em.close();
        }

        emf.close();
    }
}

2번 조회하는 경우(1차 캐시에 값이 없을 때)

쿼리가 1번만 나간다.
처음 조회 시 쿼리가 나가고 2번째 조회 부터는 1차캐시에 있는 값을 가져오기 때문이다.

2. 동일성 보장

findMember1이 가리키는 객체와 findMember2가 가리키는 객체가 동일 하다. (둘 다 1차캐시에 있는 값을 가리킴)

3. 트랜잭션을 지원하는 쓰기 지연

em.persist 가 아닌 tx.commit 시점에 쿼리가 날라가는 것을 알 수 있다.

persistence.xml

위와 같은 경우 10개가 모인 경우 commit 한다.
버퍼링을 모아서 DB에 write 할 수 있다.

4. 변경 감지(dirty checking)

flush란?

em.flush() - 직접 호출
transaction commit, JPQL 쿼리 실행 - 자동호출

영속성 컨텍스트의 변경 내용을 DB에 반영
DB transaction이 commit 될 때 플러시 발생 된다고 생각하면 됨

flush발생 시

변경감지

수정된 엔티티 쓰기 지연 SQL 저장소에 등록

쓰기 지연 SQL 저장소의 쿼리를 DB에 전송

1차 캐시는 지워지지 않는다

  1. 지연 로딩

준영속성 상태란?

원래 영속성 상태였다가 영속 컨텍스트에서 분리된 상태
즉, 영속성 컨텍스트가 더는 관리하지 않는 엔티티
영속성 컨텍스트가 제공하는 기능 사용 불가

준영속 상태로 만드는 방법

em.detach(entity) -> 특정 엔티티만 준영속 상태로 전환
em.clear() -> 영속성 컨텍스트 완전히 초기화
em.close() -> 영속성 컨텍스트를 종료

준영속 엔티티를 수정하는 방벙

변경 감지 기능 사용 (추천)
병합 사용(비추)

1.변경감지 기능 사용(DirtyChecking)

findItem으로 찾아온 값들은 영속 상태이다 => 메소드 호출 필요 없음
Transactional 어노테이션에 의해 commit이 되고 commit이 됨으로써 flush를 날린다.(flush될 때 더티채킹)

2.병합 기능 사용(merge)

준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 기능이다.

merge는 그림1과 같은 역할을 함
그림1에 itemId로 영속성 컨텍스트에서 DB를 뒤져 Item을 찾는다.
merge에 있는 item파라미터의 값으로 모두 바꿔치기를 한다.
바꿔치기를 함으로써 transantion commit 시 반영이 된다.
=> merge는 그림1의 코드를 한줄로 처리 해줌

참고

결론

코드 상으로 봤을때 merge를 사용하면 더 간결할 것 같다.
하지만 merge에는 치명적인 단점이 있어서 실제로는 사용하지 않는 것이 좋다. 변경 감지를 사용한다면 원하는 속성만 선택해서 변경이 되지만, 만일 병합을 사용한다면 모든 속성이 파라미터로 들어온 값으로 변경되기 때문이다.
=> 병합시 값이 없다면 null값이 들어간다.

0개의 댓글