LUMI_dev

Entity - #4 영속성 컨텍스트의 기능 1. 1차 캐시 본문

스파르타 코딩 클럽 | 자바 심화 과정/Spring Master (입문 주차)

Entity - #4 영속성 컨텍스트의 기능 1. 1차 캐시

luminous_dev 2025. 2. 3. 22:02

영속성 컨텍스트

: Entity 객체를 효율적으로 쉽게 관리하기 위해 만들어진 공간


영속성 컨텍스트는 어떻게 Entity 객체를 효율적으로 관리하고 있을까?

 

영속성 컨텍스트는 내부적으로 캐시 저장소를 가지고 있음 

우리가 저장하는 Entity 객체들이 1차 캐시 (캐시 저장소)에 저장

 

캐시 저장소는 Map 자료 구조 형태로 되어 있음

영속성 컨텍스트는 캐시 저장소 key에 저장한 식별자 값을 사용하여 Entity 객체를 구분하고 관리

캐시 저장소 내용
key @Id로 매핑한 기본 키 (=식별자 값) 저장
value 해당 Entity 클래스의 객체 저장

영속성 컨텍스트의 캐시 저장소 활용 방법

 

준비

더보기

PersistenceTest Class 만들기 및 세팅 


 

 

Entity 저장

em.persist(memo);  메서드가 호출되면 memo Entity 객체를 캐시 저장소에 저장

 

테스트 코드

더보기
@Test
@DisplayName("1차 캐시 : Entity 저장")
void test1() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {

        Memo memo = new Memo();
        memo.setId(1L);
        memo.setUsername("Robbie");
        memo.setContents("1차 캐시 Entity 저장");

        em.persist(memo);

        et.commit();

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}

em > persistenceContext > entitiesBykey를 확인해보면 key-value 형태로 정보가 저장되어있음


Entity 조회

em.find (내가 찾고자 하는 EntityClass 타입, PK값) 

1. 캐시 저장소에 조회하는 Id가 존재하지 않은 경우

 

a. 캐시 저장소 조회 > b. DB SELECT 조회 후 캐시 저장소에 저장 

  • em.find(Memo.class, 1); 호출 시 캐시 저장소를 확인 
  • 해당 값이 없으면 DB에 SELECT 조회 후 해당 값을 캐시 저장소에 저장하고 반환
@Test
@DisplayName("Entity 조회 : 캐시 저장소에 해당하는 Id가 존재하지 않은 경우")
void test2() {
    try {

        Memo memo = em.find(Memo.class, 1);
        System.out.println("memo.getId() = " + memo.getId());
        System.out.println("memo.getUsername() = " + memo.getUsername());
        System.out.println("memo.getContents() = " + memo.getContents());


    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        em.close();
    }

    emf.close();
}

 

 

2. 캐시 저장소에 조회하는 Id가 존재하는 경우 

 

값이 있다면 해당 Entity 객체를 반환 

 

@Test
@DisplayName("Entity 조회 : 캐시 저장소에 해당하는 Id가 존재하는 경우")
void test3() {
    try {

        Memo memo1 = em.find(Memo.class, 1);
        System.out.println("memo1 조회 후 캐시 저장소에 저장\n");

        Memo memo2 = em.find(Memo.class, 1);
        System.out.println("memo2.getId() = " + memo2.getId());
        System.out.println("memo2.getUsername() = " + memo2.getUsername());
        System.out.println("memo2.getContents() = " + memo2.getContents());


    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        em.close();
    }

    emf.close();
}

 

SELECT문 한번 날리고 결과 전달

 

<1차 캐시 장점>
1. DB 조회 횟수 줄임
2. 객체 동일성 (= DB row 1개 당 객체 1개 사용) 보장 

객체 동일성 보장 코드
@Test
@DisplayName("객체 동일성 보장")
void test4() {
    EntityTransaction et = em.getTransaction();

    et.begin();
    
    try {
        Memo memo3 = new Memo();
        memo3.setId(2L);
        memo3.setUsername("Robbert");
        memo3.setContents("객체 동일성 보장");
        em.persist(memo3);

        Memo memo1 = em.find(Memo.class, 1);
        Memo memo2 = em.find(Memo.class, 1);
        Memo memo  = em.find(Memo.class, 2);

        System.out.println(memo1 == memo2);
        System.out.println(memo1 == memo);

        et.commit();
    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}

 


memo1과 memo2가 같은지/ memo1과 memo가 같은지 확인한 결과 

1) 같은 값을 조회하는 memo1과 memo2는 == 결과 true 반환
자바에서는 객체를 두개 생성하면 주소가 달라서 다르다고 인지했었음 
근데 이건 왜 안 그러냐면, 1차 캐시에서 값을 동일하게 받아왔기 때문임 

2) memo1과 다른 값을 조회하는 memo는 == 결과 false 반환

 

 


Entity 삭제

순서 1. 삭제할 Entity 캐시 저장소 조회 > 없으면 DB에 조회해서 저장함 

em.find(클래스 타입, PK);

 

→ 

 

순서 2. 바로 delete하지 않고 deleted라는 상태로 만듦 → commit이 될 때 delete Query 호출

em.remove(삭제하고 싶은 entity);

 

 

 

@Test
@DisplayName("Entity 삭제")
void test5() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {

        Memo memo = em.find(Memo.class, 2);

        em.remove(memo);

        et.commit();

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}