영속성 컨텍스

엔티티를 영구 저장하는 환경 이라는 뜻

엔티티 매니저로 엔티티저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

@SpringBootApplication
public class JpaBasicApplication {

   public static void main(String[] args) {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa");

      EntityManager em = emf.createEntityManager();

      EntityTransaction tx = em.getTransaction();

      tx.begin();
      try{
				 //insert
				 //비영속 상태
	       **Member member = new Member();
	       member.setId(1L);
	       member.setName("test");**

				 //영속상태
	       **em.persist(member);**
				 
				 //select
		     **Member findMember = em.find(Member.class, 1L); 
				 //findMember는 persist로 영속상태에 있기때문에 1차캐시에 올라간 상태이므로
				 //해당 엔티티객체를 조회할때 DB에 접근하는 것이 아닌 1차캐시로 조회**

         tx.commit();

      }catch (Exception e){
         tx.rollback();
      }finally {
         em.close();
      }
      emf.close();

      SpringApplication.run(JpaBasicApplication.class, args);
   }

}

영속성 컨택스트의 이점

  • 1차 캐시

    em.persist(member)로 특정엔티티를 저장하면 1차캐시에 저장

    persist로 1차캐시에 저장된 데이터트렌젝션이 종료되기 전까지 남아있다.

    트렌젝션이 종료되기 전 해당 객체를 조회한다면 JPA는 내부적으로 DB의 값을 조회하는것이 아닌 우선적으로 1차 캐시를 찾는다.

    그리고 1차 캐시에 조회하고자 하는 값이 없을 경우 DB를 조회

    1차 캐시에 값이 존재 할 경우 해당객체을 출력

    따라서 DB 접근이 아닌 1차캐시에서 바로 해당 객체를 조회하기 때문에 성능적인 이점이 있음

    
    EntityTransaction tx = em.getTransaction();
    //트렌젝션 시작
    tx.begin();
    
    //비영속 상태
    Member member = new Member();
    member.setId(1L);
    member.setName("test");
    
    //영속상태
    em.persist(member); //member객체가 1차 캐시에 저장된 상태
    				 
    //select
    Member findMember = em.find(Member.class, 1L); 
    //조회하고자 하는 대상이 1차캐시에 저장된 상태이므로 db에 접근하여 조회하지않고
    //1차캐시에 저장된 데이터를 출력
    Member findMember2 = em.find(Member.class, 2L); 
    //해당 대상은 1차캐시에 저장된 상태가 아니므로 db에 접근하여 해당 데이터 출력
    
    //트렌젝션 종료
    tx.commit();

    entityManager는 보통 트렌젝션 단위로 만든다. 따라서 트렌젝션이 종료될 경우 엔티티메니저를 같이 종료시키기 때문에 클라이언트에서 요청이 들어와서 비즈니스가 끝난다면 영속성 컨택스트가 삭제되면서 1차 캐시도 사라진다. 따라서 실제 현업에서 사용될때 1차캐시를 통한 성능의 이점을 크게 얻긴 힘듦

  • 동일성 보장

    Member findMember1 = em.find(Member.class, 1L);
    Member findMember2 = em.find(Member.class, 1L);
    System.out.println("result : "+(findMember1==findMember2)); // 결과값 : true

    동일한 트랜젝션 내부에서 동일한 객체를 조회 할 경우 동일성 보장

    1차 캐시로 반복가능한 읽기 등급의 트랜잭션 격리수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공

  • 트랜잭션을 지원하는 쓰기 지연

          EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa");
    
          EntityManager em = emf.createEntityManager();
    
          EntityTransaction tx = em.getTransaction();
    
          tx.begin();
    
    			em.persist(member);
    			em.persist(member2);
    			**//여기까지 Insert sql을 데이터 베이스에 보내지 않는다.**
          
    			tx.commit();
    			**//커밋 하는 순간 데이터베이스에 Insert Sql을 보낸다.**

영속성 컨텍스트 내부에는 1차캐시 이외에 쓰기 지연 SQL저장소가 존재
하나의 트렌젝션 안에 persist가 다수 발생할 경우 해당 데이터를 1차캐시에 저장하고난 뒤
쓰기 지연 SQL저장소에 저장하고자하는 객체의 insert쿼리를 쌓아둠
commit() 하는 시점에 쓰기 지연 SQL저장소에 있던 쿼리들이 flush가 되면서 쿼리를 실행
일괄적으로 쿼리수행을 함으로서 성능적인 이점이 있음

persistence.xml파일에 하단의 옵션을 입력 할 경우 value의 사이즈 만큼 쿼리를 모아서 한번에 실행

```xml
<property name="hibernate.jdbc.batch_size" value="10"/>
```
  • 변경감지

    엔티티 수정(변경감지)

    //영속 엔티티 조회
    **Member findMember = em.find(Member.class, 1L);
    
    //영속 엔티티 수정
    findMember.setUsername("hi");
    findMember.setAge(10);
    
    // em.update(member)와 같은 코드가 필요X**
    
    //트렌젝션 종료
    tx.commit();
    • JPA에서 영속엔티티에 올라온 객체에 대한 변경을 탐지

    • 특정 영속엔티티에 수정이 발생했다면 이에 대한 update쿼리를 생성하여 db에 반영한다.

      변경 감지(Dirty checking) 동작 방식

1. JPA는 commit하는시점에 내부적으로 flush()가 호출
2. JPA는 **영속엔티티의 현재 데이터**와 **스냅샷**(DB에서 값을 읽어온 최초시점의 상태)을 **비교**
3. 변경된 엔티티 객체에 대한 update쿼리를 만들어서 쓰기지연 sql저장소에 올림
4. 업데이트쿼리를 DB에 반영
5. 커밋

엔티티의 생명주기

  • 비영속(new/transient)

    영속성 컨텍스트와 전혀 관계가 없는 새로운 형태

  • 영속(managed)

    영속성 컨텍스트에 관리되는 형태

  • 준영속(detached)

    영속성 컨텍스트에 저장되었다가 분리된 상태

  • 삭제 (remove)

    삭제된 상태

좋은 웹페이지 즐겨찾기