Persistence Context in JPA

XingXi·2023년 12월 26일
0

JPA

목록 보기
5/23
post-thumbnail

🍕 Reference

자바 ORM 표준 JPA 프로그래밍 : 교보문고
자바 ORM 표준 JPA 프로그래밍 - 기본편 : 인프런
1차 cache 그림 1
1차 cache 그림 2

🚀 Application 실행해보기

Application 에서 JPA 가 동작하는 방식

이전 포스팅대로 프로젝트 설정까지 마무리 후 새로운 자바 파일을 만들고
main method 를 정의해서 다음과 같이 작성했다.

        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

해당 main method 를 동작시키면 어떤 일이 일어날까

1. "hello"라는 이름의 persistence 정보를 찾아서 EntityManagerFactory 를 생성

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
  • EntityManagerFactory 는 EntityManager 를 만드는 객체이다. Persistence.xml 에 "hello" 를 사용하여 생성된다.
  • Application 실행 시 한번만 생성이 된다.

2. 이후 Transaction 발생 시 EntityManagerFactory 인스턴스가 EntityManager 객체를 생성한다.

  • EntityManager 인스턴스의 생명주기는 하나의 Transaction 과 동일하다.
  • EntityManager 인스턴스는 공유 되선 안된다.
  • 모든 Data 의 변형은 Transaction 안에서 이루어 져야한다.

🚀 Persistence Context

항상 적을때마다 뭔가 애매한 영속성 context 이다. 애매하다고 한 이유는
다 영어로 적어야할지, 영속성 Context 이렇게 적어야할지 전부 한글로 적을지 참 애매하다.
해당 강의 내용과 책 내용을 보며 정리한 내용이다.

JPA 의 중요한 2가지

  1. 객체와 RDB 테이블 간 Mapping 하기
  2. Persistence Context

무지성 JPA 사용 중에도 자주 듣던 말이다. 영속성 컨텍스트 1차 캐시니 뭐니 하는 말들을 들으며 알아보는 것들을 귀찮아한 내 자신이 한탄스럽다. 알고보면 이해 못 할것도 아닌데 말이다.

Persistence Context

Entity 를 영구 저장하는 환경

Entity 의 4가지 생명주기

이름특징
비영속Entity 로 선언된 객체가 새로 생성되는 경우
영속Persistence Context 에서 관리되는 상태
준영속영속성 Context 에서 분리된 상태
삭제말 그대로 삭제된 상태

🚀 Persistence Context 이해

@Entity
// Reflection 등을 JPA 에서 사용하기 때문에 기본 생성자가 존재해야한다.
public class Member
{

    public Member(Long id, String name)
    {
        this.id = id;
        this.name = name;
    }

    public Member(){}


    @Id
    private Long id;
    private String name;

    public Long getId(){ return this.id;}
    public String getName(){return this.name;}

    public void setId(Long id){this.id = id;}

    public void setName(String name){this.name = name;}
}

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

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();


        try
        {	
        	// 비영속 상태 
            Member member = new Member();
            member.setId(10L);
            member.setName("Persist Context");

            System.out.println(" Before Invoke Persistence ");
            // 영속 상태
            entityManager.persist(member);
            System.out.println(" After Invoke Persistence ");
            tx.commit();

        }
        catch (Exception e)
        {
            tx.rollback();
        }
        finally
        {
            entityManager.close();
            entityManagerFactory.close();
        }
    }

영속 상태가 되면 바로 DB 에 저장이 될까? 그렇지 않았다.
만약 Entity 가 영속 상태가 됐을 때 DB 에 저장이 되면
Before Invoke Persistence 가 선언 된 이후
Insert Query 문이 작성되고 After Invoke Persistence
이 작성되어야 할 것이다.

실행시켜보자

콘솔에 내가 정의한 문자열이 모두 출력 되고 나서 Query 문이 발생하여 DB 에 데이터가 저장이 됐다.

결론부터 말하자면

persist 로 인해 영속성에서 관리된다고 DB 에 저장되는 것이 아닌

tx.commit();

해당 트랜잭션이 종료 될 때 DB에 적용이 된다.
DB 와 Application 사이의 중간계층 역할을 수행한다

SUMMARY
영속성 Context 는 Entity 를 영구적으로 저장하는 환경으로
트랜잭션이 종료된 이후 DB 에 적용할 수 있도록 DB 와 Application 중간 계층 역할을 수행한다.


🚀 1차 Cache

위와 같이 동작하기 위해서 JPA 의 Persistence Context 에는 1차 Cache 라는 개념이 존재한다.

Entity 가 영속성 컨텍스트에서 엔티티를 관리하는 장소.
Entity 를 정의할 때 @Id 를 key 값으로 엔티티를 저장한다.

조회 순서

  1. 영속성 컨텍스트의 1차 캐시에서 해당 찾으려는 Entity 가 있는지 조회
  2. 존재하지 않는 경우 DB에 조회
  3. DB 에서 알맞은 Entity 를 찾은 경우 1차 캐시에 찾은 Entity 정보를 저장

동일한 Transaction 에 동일한 1차 Cache를 갖는다.

예시

public static void main(String[] args)
    {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

        try
        {
            Member member = new Member();
            member.setId(20L);
            member.setName("Long20");

            System.out.println(":: BEFORE :: ");
            entityManager.persist(member);
            System.out.println(":: AFTER :: ");

            Member findLong20 = entityManager.find(Member.class, 20L);
            System.out.println("findLong20.getId() : "+findLong20.getId());
            System.out.println("findLong20.getName() : "+findLong20.getName());

            tx.commit();
        }
        catch (Exception e)
        {
            tx.rollback();
        }
        finally
        {
            entityManager.close();        // entityManger 닫기
            entityManagerFactory.close(); // Entity Manager Factory 닫기
        }
    }
:: BEFORE :: 
:: AFTER :: 
findLong20.getId() : 20
findLong20.getName() : Long20
Hibernate: 
    /* insert org.example.javamain.Member
        */ insert 
        into
            Member
            (name, id) 
        values
            (?, ?)

  1. Id 가 20인 인스턴스를 생성
  2. persist -> 생성한 인스턴스를 영속화
  3. find Method 로 인스턴스를 조회

동일한 트랜잭션내에 일어나고 commit 이전에 조회를 시도 하였다.
영속성 컨텍스트의 1차 캐시에 해당정보가 존재하여 select query 문이 발생하지 않았다.

Persistence Context: 동일성 보장

위와 같은 경우 조회시 동일한 instance 를 찾기 때문에 동일한 객체에 대한 동일성을 보장 받을 수 있다.

public static void main(String[] args)
    {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

        try
        {
            Member member31 = new Member(31L, "Long31");
            entityManager.persist(member31);

            Member member1 = entityManager.find(Member.class, 31L);
            Member member2 = entityManager.find(Member.class, 31L);

            if(member1.equals(member2))
            { System.out.println("SAME"); }

            else
            { System.out.println("NOT SAME"); }

            tx.commit();
        }
        catch (Exception e)
        { tx.rollback(); }
        
        finally
        {
            entityManager.close();        // entityManger 닫기
            entityManagerFactory.close(); // Entity Manager Factory 닫기
        }
    }
SAME
Hibernate: 
    /* insert org.example.javamain.Member
        */ insert 
        into
            Member
            (name, id) 
        values
            (?, ?)

Persistence Context: Entity Dirty Check

1. Excute Code

public static void main(String[] args)
    {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

        try
        {
            Member findMemberNumber31 = entityManager.find(Member.class, 31L);
            findMemberNumber31.setName("롱삼십일");

            System.out.println("QUERY :::::::::::::::::::::::::::::: ");
            tx.commit();
        }
        catch (Exception e)
        {
            tx.rollback();
        }
        finally
        {
            entityManager.close();        // entityManger 닫기
            entityManagerFactory.close(); // Entity Manager Factory 닫기
        }
    }

2. Console Result

Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.name as name2_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?
QUERY :::::::::::::::::::::::::::::: 
Hibernate: 
    /* update
        org.example.javamain.Member */ update
            Member 
        set
            name=? 
        where
            id=?

3. DB Result

            Member findMemberNumber31 = entityManager.find(Member.class, 31L);
            findMemberNumber31.setName("롱삼십일");

영속성 컨텍스트의 1차 Cache 에 DB 에서 불러온 데이터를 저장하고
저장한 데이터를 변경하고 commit 을 진행하니 DB 에 데이터가 변경이 되었다.
update 를 선언하지 않았는데 UPDATE 쿼리가 생성되어 DB 에 적용이 되었다!

1차 cache 에 Entity 정보를 최초로 저장할 때 스냅샷을 생성한다.
이후 Commit 할 때 Entity 와 스냅샷의 데이터를 비교하여 변경된 경우 Update Query를 생성하여
DB 에 적용시킨다.

그래서 Dirty Checking

Entity Manager 가 1차 Cache 에 저장된 객체가 변경된 것을 자동으로 감지하여
DB 에 반영하는 것을 말한다


🚀 쓰기 지연 SQL

영속성 컨텍스트는 RDB 와 Application 간 중간 계층 역할을 수행하고 있다.
Transaction 이 커밋 되는 시점에 DB 에 데이터 조작이 발생하며
이를 이용하여 Buffer 처럼 사용할 수 있다.

public static void main(String[] args)
    {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

        try
        {
            Member member31 = new Member(31L, "Long31");
            Member member32 = new Member(32L, "Long32");

            entityManager.persist(member31);
            entityManager.persist(member32);


            System.out.println("QUERY :::::::::::::::::::::::::::::: ");

            // Commit 시점에 DB 로 쿼리가 발생
            tx.commit();
        }
        catch (Exception e)
        {
            tx.rollback();
        }
        finally
        {
            entityManager.close();        // entityManger 닫기
            entityManagerFactory.close(); // Entity Manager Factory 닫기
        }
    }

QUERY :::::::::::::::::::::::::::::: 
Hibernate: 
    /* insert org.example.javamain.Member
        */ insert 
        into
            Member
            (name, id) 
        values
            (?, ?)
Hibernate: 
    /* insert org.example.javamain.Member
        */ insert 
        into
            Member
            (name, id) 
        values
            (?, ?)

persist 호출 이후에 Query 가 바로 실행 되지 않고
Commit 시점에 Query가 발생한 것을 볼 수 있다.

  • entityManger 의 persist method 를 통해서 영속성 컨텍스트를 통해 객체가 관리된다.
  • 객체가 관리 될 때 1차 Cache 에 저장이 되고
  • 저장이 될 때 쓰기 지연을 위한 Query 가 생성이 되고 저장해둔다
  • 저장된 Query 는 Transaction Commit 작동하며 DB 에 적용이 된다.
  • persistence.xml 에서 hibernate.jdbc.batch_size속성을 추가하여 배치 처럼 일정 수의 쿼리가 채워지면 DB Commit 할 수 있도록 설정도 가능하다

요약

  1. JPA 의 persistence context 는 Entity 를 영구적으로 저장하는 환경을 의미한다.

  2. Entity Manager 의 persist 메소드를 통해서 Entity 가 영속화 되고 1차 Cache 에 저장이 된다.
    • 저장이 될때 해당 객체에 대한 Query 가 작성되어 따로 저장해둔다.
      - Commit 시점에 DB 에 적용이 됨으로 쓰기 지연 기능을 이용하여 Buffer 처럼 사용이 가능하다.
      - 조회 시 1차 Cache 먼저 조회를 하여 성능상의 약간의 이점과 객체지향 처럼 사용할 수 있다.
    • 1차 Cache 에 Entity 가 저장되며 최초 상태를 Snap Shot 으로 저장한다.
      - Dirty Checking 사용 하여 영속화된 객체에 변경을 감지하고 자동으로 Update Query 를 생성한다.

0개의 댓글