Everyday Dev System

영속성 컨텍스트 활용 - persist() , find() , remove() 본문

내배캠 주요 학습/Spring 입문

영속성 컨텍스트 활용 - persist() , find() , remove()

chaeyoung- 2023. 6. 14. 09:52

1. 영속성 컨텍스트란? 

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

EntityManager를 통해서 영속성 컨텍스트에 접근을 한다.

em.persist(memo); -> 컨텍스트에 저장하고 싶은 Entity Class 객체를 저장함. 

아래에서 실제로 확인하기

더보기

1. [test] - [java] - PersistenceTest.java 생성

2. 30번째 줄에 디버깅할 빨강 표시하고, 해당 메서드 Debug하기.

import com.sparta.entity.Memo;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class PersistenceTest {

    EntityManagerFactory emf;
    EntityManager em;

    @BeforeEach
    void setUp() {
        emf = Persistence.createEntityManagerFactory("memo");
        em = emf.createEntityManager();
    }

    @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.persist(memo)를 통해 컨텍스트의 캐시 저장소에 저장됨을 알 수 있다.

 

또한, entitiesByKey를 보면 HashMap으로 되어 있는걸로 봐서 캐시 저장소가 Map의 형태로 이루어져 있다는 것도 알 수 있다.

 

key에는 PK인 id가 들어간다.

value에는 memo 객체가 들어간다.

 

오류

더보기

Duplicate entry '1' for key 'memo.PRIMARY'

memo 테이블이 이미 생성되어 있어서 오류가 나는 것이므로 memo table drop하고 다시 실행.

 

1) 캐시 저장소

내부적으로 캐시 저장소가 있다.

해당 1차 캐시의 사용을 통해 DB 조회 횟수를 줄이는 장점이 있다.

우리가 저장하는 Entity 객체들이 1차 캐시 즉,캐시 저장소에 저장된다.

더보기

아래와 같은 Map 자료구조 형태의 1차 캐시

@id Entity LoadedState
1 memo1  
2 memo2  

- 영속성 컨텍스트는 캐시 저장소에서 key에 저장한 식별자값을 사용하여 객체를 구분한다.

 

 

 

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

 

1. Entity 조회 및 저장

- em.find(내가 찾고자하는 Entity Class type, PK) -> DB에서 조회해와서 1차 캐시에 저장함.

- em.persist(memo3); -> 매개변수로 받은 객체를 영속성 컨텍스트에 저장한다.

commit을 해야 실제로 반영된다.

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

1) 캐시 저장소에 조회하는 id가 존재하지 않는 경우

@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();
}

 

em.find()를 했는데 1차 캐시에 없을 경우 DB에 select해와서 정보를 가져와서 1차 캐시에 저장했다.

 

 

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

@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();
}

find해서 찾아온 다음에 1차 캐시에 저장을 한 후에 다시 한번 조회  

 

 

3) 객체 동일성 보장

- System.out.println(memo1 == memo2); -> 같은 DB의 내용이므로 객체 동일성을 보장한다.

@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();
}

 

 

 

2. Entity 삭제

- 삭제 할 Entity를 조회한 후 캐시 저장소에 없다면 DB에 조회해서 저장한다. 

- 1차 캐시에 저장된 row를 deleted 상태로 만든 후에 commit을 하여 DB에서도 삭제한다.

em.find(); 

em.remove(삭제할 객체);

- 1차 캐시에 해당하는 row에 deleted라는 상태로 만들었다가 마지막 commit시에 delete query가 수행되어 DB에서도 삭제된다.

- 아래 코드를 디버깅하여 보면 영속성 컨텍스트에 Deleted로 관리하고 있음을 알 수 있다.

- commit하면 사라지고 delete 쿼리문이 실행.

@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();
}

 

'내배캠 주요 학습 > Spring 입문' 카테고리의 다른 글

JPA 활용시 _(언더바)의 예약어 역할  (0) 2023.06.15
영속성 컨텍스트의 기능 - 3가지  (0) 2023.06.14
영속성 컨텍스트  (0) 2023.06.14
ORM과 JPA 활용  (0) 2023.06.14
@Service와 @Repository  (0) 2023.06.13