JPA 영속성 관리

gotcha!!·2023년 9월 8일
0

JPA

목록 보기
4/16

JPA에서 중요한 점 2가지를 뽑는다면,

  • 객체와 관계형 데이터 베이스 매핑하기
  • 영속성 컨텍스트

위 두 가지가 존재한다.

여기서 영속성 컨텍스트에 대해 좀 기록하려고 한다.

영속성 컨텍스트

영속성 컨텍스트는 JPA를 이해하는데 중요한 용어이다.
"엔티티를 영구 저장하는 환경"이라는 뜻으로 EntityManager.persist(entity);
이렇게 쓸 수 있다.

정리하자면..
영속성 컨텍스트는 엔티티 매니저를 통해서 접근이 가능하고, 눈에 보이지 않으며, 논리적인 개념이다.

Entity의 생명주기

  • 비영속(new/transient)
    영속성 컨텍스트와 전혀 관계가 없는 새로운 상태이다.
// 비영속 (객체를 생성한 상태)
Member member = new Member();
member.setId(1L);
member.setName("helloWorld")
  • 영속(manged)
    영속성 컨텍스트에 관리되는 상태

// 비영속 (객체를 생성한 상태)
Member member = new Member();
member.setId(1L);
member.setName("helloWorld")

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

/* 영속상태
실제 디비에 저장이 되지 않는다.
바로 디비에 쿼리가 날아가지 않는다.
현재 영속성 컨텍스트 1차 캐시에 있는 상황
*/

em.persist(member);
  • 준영속(detached)
    영속성 컨텍스트에 저장되었다가 분리된 상태
em.detach(member);
  • 삭제(removed)
    말 그대로 삭제된 상태
em.remove(member);

영속성 컨텍스트의 이점

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

1차 캐시

비영속 상태로 객체를 생성하고, 엔티티 매니저를 통해 영속성 컨텍스트에 엔티티를 담게 되면 1차 캐시에 데이터가 들어 간 상태이다.

// 비영속 상태
Member member = new Member(1L, "HelloWorld");

// 영속 상태(1차 캐시에 데이터 저장됨)
em.persist(member);

// 1차 캐시에서 데이터를 조회
// 실제로 디비에 select 쿼리를 보내지 않고, 1차 캐시에서 데이터를 찾는다.
Member findMember = em.find(Member.class, 1L);

그러나 만약에 1차 캐시에 값이 존재하지 않는다면..?

1차 캐시에서 값을 우선적으로 조회하고, 값이 없을 경우에는 DB에 데이터를 조회하고 값을 가져오게 된다.
이 때, 해당 데이터는 1차 캐시에 저장이된다.

동일성 보장

Member a = em.find(Member.clas, 1L);
Member b = em.find(Member.class, 1L);

System.out.println(a == b) // true

같은 트랜잭션 내에서 비교했을 때, a와b는 같은 식별자이기에 "==" 비교에서 true가 나올 수 있다.

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

쓰기 지연

EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();

Member member = new Member(1L, "hello")
em.persist(member);

// 여기까지 1차 캐시(영속성 컨텍스트)에 member 엔티티가 저장되어있다.
하지만 트랜잭션에서 커밋을 실행하지 않았기에 아무리 영속성 컨텍스트에 저장되어있어도 DB로 INSERT SQL을 보내지 않는다.

tx.commit();
// 커밋을 하는 순간, 쓰기 지연 SQL저장소에서 flush하여, INSERT SQL을 보낸다.
// 이렇게 member 엔티티가 DB에 저장되게 된다.

만약 em.persist(memberA) em.persist(memberB) 이렇게 여러개가 영속성 컨텍스트에 담겨져있으면 어떨까?

해당 엔티티들이 영속성 컨텍스트 내에 저장이 되고 쓰기 지연 SQL 저장소에는 INSERT SQL이 쌓이게 된다.

그래서 커밋을 하는 순간

한꺼번에 INSERT SQL이 flush되게 된다.

변경 감지(dirty checking)

// 영속 엔티티 조회
Member member = em.find(Member.class, 1L);

// 영속 엔티티 데이터 수정하기
member.setName("Lee");

// 이렇게 이름이 수정되었다고 가정했을 때,
// em.update(member);
// 이렇게 엔티티 매니저를 통해서 update sql을 날려야하지 않을까?
// 대답은 "아니오" 이다.

tx.commit();

변경 감지라는 것은 1차 캐시에 값을 저장할 때, 해당 값이 스냅샷에 남게 된다.
그 상태에서 1차 캐시의 값이 변경이 되었다면, 1차 캐시와 스냅샷의 값이 다르기에 JPA가 그걸 파악하고
쓰기 지연 SQL 저장소에 UPDATE SQL을 작성하여 커밋시점에 flush를 해서 DB에 데이터를 변경해주게 된다.

준영속 상태

JPA가 따로 엔티티를 관리하지 않는 상태이다.

  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태
  • 영속성 컨텍스트가 제공하는 기능을 사용 못함
em.detach(entity)
// 특정 엔티티만 준영속 상태로 전환

em.clear();
영속성 컨텍스트를 완전 초기화

em.close()
영속성 컨텍스트 종료

엔티티 삭제

// 대상 조회
Member member = em.find(Member.class, 1L);

em.remove(member); // 엔티티 삭제
profile
ha lee :)

0개의 댓글