ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] 영속성 컨텍스트
    Backend/Spring 2022. 8. 22. 15:38

    Entity, EntityManagerFactory, EntityManagerFactory

    Entity

    RDB의 Table과 매핑되는 객체이다.

    @Entity
    @Table(name = "customers") // table 명이 클래스명과 다를경우
    public class Customer {
        @Id // Id PK
        private long id;
        private String firstName;
        private String lastName;
    
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    }

     

    EntityManagerFactory

    Entity를 관리하는 EntityManager를 생산하는 공장이다.

    Thread Safe하다. 하나의 빈에 대해 여러 쓰레드가 접근해도 안전하게 동작한다.

     

    EntityManager

    EntityManager는 Entity를 저장하고, 수정하고, 삭제하고, 조회하는 (CRUD) 등 Entity와 관련된 모든 일을 처리한다.

    Thread Safe하지 않다. 여러 Thread에서 동시에 접근할 경우 동시성 이슈가 발생할 수 있다. 

    • 엔티티 매니저는 트랜잭션을 시작할 때, 커넥션을 획득한다.

     

    영속성 컨텍스트(Persistence Context)

    영속성 컨텍스트란? 

    JPA를 이용하는데 가장 중요한 요소로 엔티티를 영구 저장하는 환경이라는 뜻이다.

    엔티티 매니저는 엔티티를 영속성 컨텍스트에 보관하고 관리한다.

    • 엔티티는 엔티티매니저에 의해 영속성 컨텍스트에서 관리된다.

     

    특징

    • 영속성 컨텍스트와 식별자 값
      • 영속성 컨텍스트 안에서 관리되는 엔티티는 식별자 값을 반드시 가져야한다.
      • key-value로 엔티티를 관리하기 때문이다. (1차 캐시 관리를 위함)
    • 영속성 컨텍스트와 데이터 베이스 저장
      • JPA는 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 DB에 반영한다.(FLUSH)
      • 플러시(flush)는 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 작업인데, 이때 등록, 수정, 삭제 등의 엔티티 변경사항을 DB에 반영한다.
    • 영속성 컨텍스트가 엔티티를 관리함으로써 얻는 이점
      • 1차캐시
      • 동일성 보장 (1차 캐시로 인한 현상)
      • 트랜잭션을 지원하는 쓰기 지원
      • 변경 감지 (durty checking)
      • 지연 로딩

     

    엔티티 생명주기

    • 비영속 (new / transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태
    • 영속 (managed) : 영속성 컨텍스트에 저장된 상태
    • 준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
    • 삭제 (removed) : 삭제된 상태

    • new로 생성된 entity가 엔티티 매니저에 의해 persist를 하게 되면 managed(영속) 상태가 됨
    • flush가 이루어지면 DB에 연산이 이루어짐
    • 관리되던 영속 객체를 detach하게 되면 준영속 상태로서 더이상 관리되지 않는 상태가 됨
    • 영속 컨텍스트를 clear해주게 되면 관리되던 영속 객체를 모두 지워버려 영속성 컨텍스트 안에서 관리되면 entity들이 모두 준영속 상태로 빠져나오게 됨
    • close는 영속성 컨텍스트를 완전히 종료시켜 관리되던 entity들을 모두 준영속 상태로 만들고 영속성 컨텍스트가 종료됨
    • 준영속 객체를 merge를 통해 영속화 시킬 수 있음
    • remove를 통해서 영속화된 객체를 삭제하고 다시 persist를 통해서 영속화 시킬 수 있음

    비영속

    // 객체가 영속성컨텍스트, 데이트베이스와 무관한 상태이다.
    Customer customer = new Customer(); 
    customer.setId(1L);
    customer.setFirstName("honggu");
    customer.setLastName("kang");

    영속

    Customer customer = new Customer();
    customer.setId(1L);
    customer.setFirstName("honggu");
    customer.setLastName("kang");
    
    // customer객체가 영속성 컨텍스트에서 관리된다.
    // em = EntityManager
    em.persist(customer);

    준영속

    // 영속상태의 customer객체(엔티티)를 영속성컨텍스트에서 분리한다.
    em.detach(customer);
    // 영속상태의 모든 객체를 영속성컨텍스트에서 분리한다.
    em.clear()
    // 영속성컨텍스트를 종료한다.
    em.close()

    삭제

    // customer 엔티티를 영속성컨텍스트에서 분리하고, DB에서도 삭제한다.
    em.remove(customer)

     

    Customer 엔티티를 통한 영속성 컨텍스트 이해

    저장

    EntityManager em = emf.createEntityManager(); // 1)엔티티 매니저 생성
    
    EntityTransaction transaction = em.getTransaction(); // 2)트랜잭션 획득
    transaction.begin(); // 3)트랙잰셕 begin
    
    Customer customer = new Customer(); // 4-1)비영속
    customer.setId(1L);
    customer.setFirstName("honggu");
    customer.setLastName("kang");
    
    em.persist(customer); // 4-2)영속화
    
    transaction.commit(); // 5)트랜잭션 commit
    // 트랜잭션이 커밋이 되는 순간 쿼리가 수행된다. flush DB와 동기화가 된다.

    조회

    1차 캐시를 이용한 조회

    @Test
    void 조회_1차캐시_이용() {
    	EntityManager em = emf.createEntityManager();
        EntityTransaction transaction = em.getTransaction();
        transaction.begin();
    
        Customer customer = new Customer();
        customer.setId(1L);
        customer.setFirstName("honggu");
        customer.setLastName("kang");
    
    	em.persist(customer);
        transaction.commit();
    
        Customer entity = em.find(Customer.class, 1L); // 1차 캐시에서 조회한다.
        log.info("{} {}", entity.getFirstName(), entity.getLastName());
    }
    • em에서 find를 할 경우 1차 캐시에 정보가 있으면 1차 캐시에서 조회됨

    • DB에 질의하지 않고 1차 캐시에서 결과를 반환

     

    DB를 이용한 조회

    @Test
    void 조회() {
    	EntityManager em = emf.createEntityManager();
    	EntityTransaction transaction = em.getTransaction();
      transaction.begin();
    
    	Customer customer = new Customer();
      customer.setId(2L);
      customer.setFirstName("guppy");
      customer.setLastName("hong");
    
      em.persist(customer);
      transaction.commit();
    
      em.clear(); //영속성 컨텍스트를 초기화 한다.
    
      Customer entity = em.find(Customer.class, 2L); // DB 에서 조회한다. SELECT ...
      log.info("{} {}", entity.getFirstName(), entity.getLastName());
      em.find(Customer.class, 2L); // SELECT Query 수행되지 않는다. 1차캐시 사용
    }
    • 1차 캐시에 고객 정보가 없기 때문에 영속성 컨텍스트는 DB에 질의하여 결과를 가져와 1차 캐시에 캐싱함

    • DB에 질의하여 1차 캐시에 저장한 후에 결과를 반환한다.

    수정

    @Test
    void 수정() {
        EntityManager em = emf.createEntityManager();
        EntityTransaction transaction = em.getTransaction();
        transaction.begin();
    
        Customer customer = new Customer();
        customer.setId(1L);
        customer.setFirstName("honggu");
        customer.setLastName("kang");
    
        em.persist(customer);
        transaction.commit();
        // 엔티티를 영속화한후, 커밋을해서 flush()를 통해 DB에 저장.
    
        transaction.begin();
    
        Customer entity = em.find(Customer.class, 1L);
        entity.setFirstName("guppy");
        entity.setLastName("hong");
    
        // em.update(entity) ??!!
        transaction.commit(); // flush -> UPDATE ... 
    }

    • 변경감지 - durty checking
      • JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해 두는데 이것을 스냅샷이라 한다.
      • 플러시 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다.
      • 만일 스냅샷과 비교하여 변경된 내용이 있을 경우 update Query를 수행한다.
      • 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용이 된다.

     

    삭제

    @Test
    void 삭제() {
        EntityManager em = emf.createEntityManager();
        EntityTransaction transaction = em.getTransaction();
        transaction.begin();
    
        Customer customer = new Customer();
        customer.setId(1L);
        customer.setFirstName("honggu");
        customer.setLastName("kang");
    
        em.persist(customer);
        transaction.commit();
    
        transaction.begin();
    
        Customer entity = em.find(Customer.class, 1L);
        em.remove(entity);
        
        transaction.commit(); // flush -> DELETE ..
    }

     

    댓글

Designed by Tistory.