JPA

영속성 관리

스파이더웹 2022. 8. 26. 23:33
728x90
반응형

영속성 컨텍스트란?

"엔티티를 영구 저장하는 환경"

 

엔티티의 생명주기

 

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

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

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

삭제 - 삭제된 상태

 

// ..기존 코드동일 
 
	try {
	//비영속 상태
            Member member = new Member();
            member.setId(100L);
            member.setName("HelloJPA");

	//영속 상태
            em.persist(member);

            tx.commit();
     	}

단순 객체만 생성하여 set을 했을때는 영속성 컨텍스트와 전혀 관계가 없는 상태이다. 이후 em.persist() 로직이 구현된 후 엔티티 매니저의 영속성 컨텍스트를 통해 영속상태로 변경된다

 

영속상태가 되었다고 해서 DB의 쿼리가 실행되는 것은 아니다 그럼 언제 실행되냐? 트랜잭션 커밋 시점에 DB의 쿼리가 실행된다 

try {
            Member member = new Member();
            member.setId(100L);
            member.setName("HelloJPA");

            System.out.println("======before=======");
            em.persist(member);
            System.out.println("======after=======");

            tx.commit();
       }

이미지에서도 알 수 있듯이, 영속성 컨텍스트에 저장되었다고해서 쿼리가 실행되지않고 이후에 실행되는것을 확인할 수 있다.

728x90

영속성 컨텍스트의 이점

  • 1차 캐시
  • 동일성 보장
  • 트랜잭션을 지원하는 쓰기 지연 
  • 변경 감지
  • 지연 로딩

1차 캐시 - 조회에서의 이점

영속성 컨텍스트를 통해 1차캐시의 조회에서 이점은 무슨 말일까? 쉽게 말하면 DB에서 값을 가져오지 않고 1차 캐시에서 값을 가져온다 생각하면 된다(대단한 성능상의 차이는 없음)

 

코드에서 보면 Member 객체를 생성 이후 값을 넣어 영속성 상태로 만들어 주었다. 이후 Member객체를 조회한 것이다 일반적인 생각이면 em.persist(); 를 통해 DB의 Insert 쿼리가 실행되었을 것이고, em.find();를 통해 select 조회 쿼리가 실행되었을 것이라 생각할 것이다.

 try {
            Member member = new Member();
            member.setId(101L);
            member.setName("HelloJPA");

            System.out.println("======before=======");
            em.persist(member);
            System.out.println("======after=======");

            Member findMember = em.find(Member.class, 101L);
            System.out.println("findMember.getId() = " + findMember.getId());
            System.out.println("findMember.getName() = " + findMember.getName());

            tx.commit();
        }

 

하지만 콘솔창에 결과내용을 보면 DB Insert 쿼리의 예상시점도 다르거니와 심지어 select 조회 쿼리는 실행조차 되지않았다. Insert 쿼리야 commit() 시점의 실행된다고 위에서 말을 했으니 넘어가겠지만 값을 가져오는 조회는 DB를 실행조차 안했다. 그 이유가 1차 캐시 에서 값을 가져왔기 때문이다 em.find()를 통해 먼저 1차 캐시에서 값을 찾는다 값이 있으면 1차캐시에서 반환하고 없으면 DB에서 값을 반환하는데, 바로 반환하지않고 1차캐시에 값을 저장한 후 반환한다

그러므로 두번째부터 동일한 값을 조회하는 경우 1차캐시에서 값을 가져온다(즉, 2회차부터는 select 쿼리가 실행되지 않는다)

  try {
            
            Member findMember1 = em.find(Member.class, 100L);
            Member findMember2 = em.find(Member.class, 100L);
          
           tx.commit();
        }
        
        //findMember1은 1차캐시에 저장되어있지않아, DB에서 값을 반환받고
        //findMember2 부터 1차캐시에 저장되어있는 값을 반환 받는다

 

select 쿼리가 1번만 실행

 

영속 엔티티의 동일성 보장

em.find를 통해 객체의 가져올 때 처음만 DB에서 값을 가져오고 이후 부터는 1차 캐시에서 값을 가져오기에 동일한 객체를 보장 받는다 

Member m1 = em.find(Member.class, 100L);
Member m2 = em.find(Member.class, 100L);

System.out.println(m1==m2); //true 리턴

 

 

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

해당 내용은 위에서 말한 em.persist(); 로직을 실행했다고 Insert 쿼리를 DB의 보내지 않고 커밋한 순간에 DB의 Insert 쿼리를 보낸다 

이미지에서 처럼 persist(); 실행 된 경우 1차캐시와 함께 쓰기 지연 SQL 저장소에 정되고 이후 commit 시점의 쿼리가 실행된다

변경 감지

변경 감지는 쉽게 엔티티 수정을 의미한다. 값을 가져오면 영속성 컨텍스트 1차캐시에 저장하는데, 처음 가져온 내용을 스냅샷으로 저장하여, 스냅샷과 비교하여 가져온 데이터의 변경된 내용이있으면 UPDATE 쿼리를 SQL 저장소에 저장해두었다가 commit 시점의 update 쿼리가 DB로 실행된다

 

플러시(영속성 컨텍스트를 비우는 것이 아니다)

영속성 컨텍스트의 변경내용을 DB의 반영 - 즉, 영속성 컨텍스트의 쿼리들을 DB에게 날려준다

728x90
반응형