[JPA] JPA 영속성 컨텍스트

아는벌·2023년 3월 8일
0

web (2)

목록 보기
17/20

영속성

영속성 컨텍스트

  • 엔티티 객체들을 관리하는 일종의 컨테이너
  • 엔티티를 영구 저장하는 환경
  • EntityManager을 생성할 때 자동으로 생성
  • 영속성 컨텍스트에 등록된 엔티티는 엔티티 메니저를 통해 영속성 컨텍스트에 접근/관리 가능

상태

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

생명주기

비영속

  • 엔티티 객체를 생성만 했고 아직 엔티티를 영속성 컨텍스트에 저장 X
  • 순수한 객체 상태
  • 영속성 컨텍스트와 데이터베이스와 무관한 상태

영속

  • EntityManager를 통해 엔티티가 영속 컨텍스트에 저장된 상태
  • 영속성 컨텍스트가 관리하는 엔티티를 영속 상태(영속성 컨텍스트에 의해 관리됨)
  • 엔티티를 영속 상태로 만들기 위해 em.persist()메소드 사용, em.find()를 통해서도 영속 상태로 만들 수 있음

준영속

  • 영속성 컨텍스트가 관리하던 영속 상태의 엔티티를 영속성 컨텍스트가 관리하지 않는 상태
  • 엔티티가 영속성 컨텍스트를 벗어나서 값을 수정해도 데이터베이스에 영향이 없음
  • 준영속 상태의 엔티티는 메모리에서 완전히 사라진 것이 아님
  • em.merge()로 준영속 -> 영속 상태로 전환 가능
  • em.detach(entity) - 준영속 상태 명시적 호출, 특정 엔티티를 영속성 컨텍스트에서 분리
  • em.cloase() - 영속성 컨텍스트를 닫음, 영속석 컨텍스트가 관리하던 모든 엔티티들을 준영속 상태로 전환
    -em.clear() - 영속성 컨텍스트 초기화, 영속석 컨텍스트가 관리하던 모든 엔티티들을 준영속 상태로 전환

삭제

  • 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제
  • em.remove(..)

특징

  • 영속성 컨텍스트가 엔티티를 관리하는 것의 장점
    - 1차 캐시
    • 동일성 보장
    • 트랜잭션을 지원하는 쓰기 지연
    • 변경 감지
    • 지연 로딩

1차 캐시

  • 영속성 컨텍스트는 내부에 캐시를 가지고 있음
  • 영속 상태의 엔티티는 모두 여기에 저장
  • Map 형태로 키 - @Id로 식별한 식별자, 값 - 엔티티 인스터스
  • persist() 메소드 -> 1차 캐시에 등록됨
  • 1차 캐시에 등록된 엔티티는 트랜잭션 종료될 때 DB에 반영(플러시)

SQL 저장소

  • 영속성 컨텍스트는 내부에 SQL 저장소를 가지고 있음

1) persiste(memberA) - 영속 컨텍스트 1차에 엔티티 등록
2) 엔티티에 설정된 값을 기반으로 INSERT SQL 명령어 실행
3) 쓰기 지연 SQL 저장소에 등록

엔티티 조회

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

em.find() 호출
1차 캐시에서 식별자 값으로 엔티티를 찾음

  • 있음! - 1차 캐시에서 조회
    • 메모리에 있는 1차 캐시에서 엔티티 조회
  • 없음! - DB에서 조회
    • DB를 조회해서 엔티티를 생성
    • 1차 캐시에 저장한 후에 영속성 상태의 엔티티 반환

영속 엔티티의 동일성 보장

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

System.out.println(a == b); // 동일성 비교
  • 결과는 참, 같은 인스턴스임
  • 영속성 컨텍스트는 성능상 이점과 엔티티의 동일성을 보장!

엔티티 등록

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

// 엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야 한다.
transaction.begin();    // 트랜잭션 시작

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

// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit();   // 트랜잭션 커밋
  • 엔티티 매니저는 트랜잭션을 커밋하기 전까지 데이터베이스의 엔티티를 저장하지 않음
  • SQL에 저장소에 모아뒀다가 트랜잭션 커밋 시 DB로 보냄(쓰기 지연!)

엔티티 수정

1) 트랜잭션 커밋 -> 엔티티 매니저 내부에서 먼저 플러시 호출
2) 엔티티와 스냅샷을 비교해서 변경된 엔티티 찾기
3) 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소로
4) 쓰기 지연 저장소의 SQL을 데이터베이스로 보냄
5) 데이터베이스 트랜잭션을 커밋

  • 스냅샷: 특정 객체의 상태를 저장하는 것
  • JPA의 UPDATE는 기본적으로 모든 칼럼 수정을 원칙
  • 모든 경우의 UPDATE를 하나의 SQL로 처리 가능

엔티티 삭제

Member memberA = em.find(Member.class, 100L);  // 삭제 대상 엔티티 조회
em.remove(memberA);     // 엔티티 삭제
  • 삭제 대상 엔티티 조회 후 삭제
  • 엔티티를 즉시 삭제하는 것이 아님
  • 삭제 쿼리를 SQL 저장소에 등록
  • em.remove(memberA); 호출 시 영속성 컨텍스트에서 제거

플러시(flush)

플러시 실행

1) 변경 감지 동작, 모든 엔티티를 스냅샷과 비교
2) 수정된 엔티티는 수정쿼리 생성 후 쓰기 지연 SQL 저장소 등록
3) 쓰기 지연 SQL 저장소의 쿼리를 DB로 전송

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

1) em.flush() 직접 호출
2) 트랜잭션 커밋 시 플러시 자동 호출
3) JPQL 쿼리 실행 시 플러시 자동 호출

참고

https://ultrakain.gitbooks.io/jpa/content/chapter3/chapter3.html

0개의 댓글