본문 바로가기

강의/자바 ORM 표준 JPA

JPA, 영속성 컨텍스트의 이점 (변경감지)

영속성 컨텍스트의 이점


 앞서 영속성 컨텍스트의 이점은 5가지가 존재한다고 하였습니다.

• 1차 캐시

• 동일성(identity) 보장

• 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)

• 변경 감지(Dirty Checking)

• 지연 로딩(Lazy Loading)

영속성 컨텍스트 부분에는 변경 감지를 할 수 있다.

우선 코드로 한번 확인해보자.

 

 

다음은 update하는 부분을 살펴볼 것이다.

이미 database에 저장되어 있는 member를 찾고, 해당 member의 id 값을 수정하는 명령어이다.

 

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.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();

        // code
        Member member = em.find(Member.class, 150L);
        // 멤버를 찾고 name을 바꿔준다.
        member.setName("ZZZZZ");
        

        System.out.println("===========================");

        tx.commit();
        em.close();
        emf.close();
    }
}

 

commit을 할 때 update query가 나가는 것을 확인할 수 있다.

 

 


일반적으로 생각하기에는 엔티티 값을 바꾸고 나면,

값을 저장하는 명령어를 이용하여 변경된 내용을 update 하라고 명령어를 보내야 할 것 같은데

JPA는 알아서 update query를 생성하고 발송한다.

 

어떻게 해서 이러한 현상이 발생하는 것일까?

 

JPA 목적이 자바 컬렉션을 다루듯이 객체를 다루도록 할ㄹ 수 있게 만드는 것이 목적이다.

그렇기 때문에 변경을 하였다고 하여 em.persist()를 할 필요가 없다.

 

이런 현상의 원리는?

값만 바꿨는데 업데이트가 되는 원리는 무엇일가?

JPA는 변경감지라는 기능 (dirty checking)이 제공된다.

 

commit을 하면 엔티티와 스냅샷을 비교하는 과정을 진행한다.

변경된 사항은 쓰기 지연 SQL 저장소에 저장이 되고, commit이 되기 전 내부적으로 flush가 호출이 되어 변경된 사항이 query로 발송이 된다.

 

스냅샷은 최초 영속성 컨텍스트에 들어온 상태를 찍어둔 것이다.
commit 되는 상태에 JPA가 1차 캐시에서 Entity와 스냅샷의 값을 비교하고, 값이 다르다면 update query를 쓰기 저장소에 저장하고
commit 되는 시점에 db에 반영한다.

 

flush란?


영속성 컨텍스트 변경내용을 데이터 베이스에 반영하는 것이다.

flush를 한다고 해도 1차 캐시는 유지가 된다.

영속성 컨텍스트에 저장되어 있는 쓰기 지연 SQL 저장소에 저장되어 있는 query들이 database에 반영되는 과정이다.

 

영속성 컨텍스트를 플러시 하는 방법

em.flush() - 직접 호출

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

JPQL 쿼리 실행 - 플러시 자동 호출

 

JPQL 쿼리 실행시 플러시가 자동으로 호출되는 이유


em.persist를 이용하여 memberA, memberB, memberC를 1차 캐시에 저장하였다.

commit을 하기 전 em.createQuery를 이용하여 member들을 조회 하는 경우 자동으로 플러시 호출이 되지 않는다면

member를 조회하지 못하는 상황이 발생한다.

이러한 문제를 방지하기 위해 JPQL 쿼리 실행시에는 플러시가 자동으로 호출된다.

 

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();