[JPA] 영속성 관리

gyeol·2024년 4월 2일

JPA

목록 보기
2/13
post-thumbnail

김영한 님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편'를 듣고 적은 글 입니다.

엔티티 매니저 팩토리와 엔티티 매니저


고객의 요청이 올때마다 EntityManager를 생성한다. 이를 통해 디비 접근할 수 있다.

엔티티 매니저 안에 영속성 컨텍스트 공간이 존재한다고 생각하면 된다.

영속성 컨텍스트

JPA를 이해하는데 가장 중요한 용어이다. 영속성 컨텍스트는 논리적인 개념으로 실제 눈에 보이진 않는다. 앞서 이야기 한 것 처럼 엔티티 매니저를 통해 영속성 컨텍스트에 접근가능하다.

엔티티의 생명주기

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

비영속

Member member= new Member();
member.setId("member1");
member.setUsername("회원1");

단순 member 객체만 생성했기에 JPA와 전혀 관련이 없다.

영속

Member member= new Member();
member.setId("member1");
member.setUsername("회원1");

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

//객체 저장(영속)
em.persist(member);

멤버 객체 생성 후 엔티티 매니저를 얻어와 persist()를 하면 영속 컨텍스트 안에 멤버 객체가 들어가게 된다.
하지만 이때 DB에 저장되진 않는다. 커밋 이후에 쿼리가 날아가기 때문이다.

em.detach(member)을 통해 회원 엔티티를 컨텍스트에서 분리해 준영속 상태로 만들수도 있고 em.remove(member)를 사용해 객체를 삭제할 수도 있다.

영속성 컨텍스트의 이점

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

1차 캐시

Member member= new Member();
member.setId("member1");
member.setUsername("회원1");

//1차 캐시에 저장
em.persist(member);

//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");

영속성 컨텍스트는 1차 캐시라는것을 가지고 있다. 데이터 조회시 DB 먼저 조회하는게 아닌 1차 캐시를 조회해서 캐시 값을 가져온다.

그런데 DB에는 있고 1차캐시에는 없으면 디비에서 조회한 후 1차 캐시에 저장하고 캐시를 반환한다.

동일성 보장

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");

System.out.println(a == b); //동일성 비교 true

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

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

영속 컨텍스트 안에 쓰기 지연 SQL 저장소 존재한다.

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();

transaction.begin();

em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않음

//커밋하는 순간 INSERT SQL 날림
transaction.commit();

persist()를 하면 1차 캐시에 저장하고 쓰기 지연 SQL 저장소에 INSERT SQL을 생성해놓는다. commit() 이후 DB에 만들어놓은 INSERT SQL을 날린다.

변경 감지

find()로 멤버 변수 찾아서 이름이나 아이디 바꿔준 뒤 persist() 적을 필요가 없다. 자동으로 업데이트 쿼리가 나가기 때문이다. 이를 Dirty Checking 이라고 한다.

스냅샷이라는 걸 찍어두는데 이때 엔티티랑 스냅샷을 비교한다. 비교하고 바뀐게 있으면 UPDATE 쿼리를 쓰기 지연 SQL 저장소에 만들어 둔다. 그리고 디비에 반영해 커밋한다.

플러시

영속성 컨텍스트의 변경내용을 데이터베이스에 반영한다.
플러시는 영속성 컨텍스트를 비우지 않는다.
트랜잭션이라는 작업단위가 매우 중요하다. (커밋 직전에만 동기화하면 된다)

플러시 모드 옵션

  • FlushModeType.AUTO : 커밋이나 쿼리를 실행할 때 플러시(DEFAULT)
  • FlushModeType.COMMIT : 커밋할 때만 플러시

플러시 발생 시

  • 변경 감지
  • 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
  • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송
    (등록, 수정, 삭제 쿼리)

영속성 컨텍스트를 플러시하는 방법

  • em.flush() : flush() 를 사용해도 1차캐시는 그대로 유지된다. 쓰기 지연 SQL 저장소에 위치한 쿼리들 중 변경된 애들이 데이터베이스에 반영이 되는 과정이라고 생각하면 된다.

  • 트랜잭션 커밋

  • JPQL 쿼리 실행 : JPQL 실행 시 무조건 flush() 를 날려서 DB에서 쿼리문을 사용할 수 있도록 한다. 아직 쿼리문에 안날아갔는데 INSERT문 으로 조회하면 오류가 발생하기 때문이다.

영속 -> 준영속

영속 상태의 엔티티가 영속성 컨테스트에서 분리한 것을 말한다.
준영속 상태가 되면 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.

준영속 상태로 만드는 방법

  • em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
    detach()를 하게되면 이름을 바꿔도 업데이트 쿼리를 날리지 않는다. 준영속 상태가 되서 더이상 JPA 에서 관리하지 않기 때문이다.

  • em.clear() : 영속성 컨텍스트를 완전히 초기화
    1차 캐시를 완전히 지워버린다.

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

profile
공부 기록 공간 '◡'

0개의 댓글