// 에드센스

이 카테고리는 인프런 김영한님의 JPA 강의를 보고 정리하는 공간입니다.

 


영속성 컨텍스트란?

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

 

  • 엄밀히 말하자면 DB에 저장하는 것이 아니라 엔티티를 영속성 컨텍스트라는 곳에 저장한다는 의미
  • 눈에 보이지 않는 논리적인 개념이다.
  • 엔티티 매니저를 통해 접근한다.
  • 엔티티 매니저와 영속성 컨텍스트가 1대 1로 매핑된다

 


엔티티의 생명주기

  • 비영속: 영속성 컨텍스트와 관련 없는 새로운 상태
  • 영속: 영속성 컨텍스트에 관리되는 상태
  • 준영속: 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 삭제: 삭제된 상태

 

 

비영속 상태

영속성 컨텍스트와 엔티티가 아무런 관련이 없음. 그저 엔티티가 새로 생성되어있는 상태일 뿐.

Member member = new Member();
member.setId("member1");
member.setUsername("Lee");

그냥 member객체를 생성했을 뿐 아무런 다른 과정이 없다.

 

 

영속 상태

생성한 엔티티를 영속성 컨텍스트에 넣음. 

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

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

//객체를 저장한 상태(영속)
em.persist(member);

 

 

준영속, 삭제 상태

준영속 상태는 영속성 컨텍스트에 있다가 detach된 상태

삭제는 삭제 상태

//회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);

//객체를 삭제한 상태(삭제)
em.remove(member);

 

 

 


영속성 컨텍스트의 장점

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

 

1차 캐시

  • find나 persist시 1차 캐시에 저장된다.
  • 영속성 컨텍스트 안에 1차 캐시 공간이 있다. PK가 Key가 되고 엔티티 그 자체가 Value가 된다.
  • JPA를 통한 조회 작업시 바로 DB에 SELECT 쿼리를 날리는 것이 아닌 1차 캐시부터 조회한다. 만약 1차 캐시가 있다면 (찾고자 하는 엔티티가 영속화 돼있다면) 쿼리문을 날리지 않고도 조회가 가능하고 캐시에 없으면 DB를 조회한다.
  • 만약 DB에서 조회를 한다면 그 엔티티는 1차 캐시에 저장된다. 
  • 요청 시작부터 트랜잭션 종료 시까지만 1차 캐시가 사용된다. 매우 짧은 순간이기에 정교한 구조가 아니면 성능상의 큰 이점을 얻기는 힘들다.
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");

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

//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
  • member1은 DB에서 조회한 것이 아닌 영속성 컨텍스트의 1차캐시에서 조회한 엔티티이다. 
  • 사실 persist만으로 DB에 저장이 되진 않는다. persist가 완료되면 영속성 컨텍스트에 저장되는 것이지 트랜잭션이 수행되어야 DB에 저장된다.

 

 

만약 1차 캐시에 없는 엔티티를 조회하려고 하면 DB에서 조회 후 1차 캐시에 영속화 한다.

 

 

 

동일성 보장

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

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

같은 트랜잭션 안에서의 같은 엔티티를 조회하면 동일한 엔티티임을 보장할 수 있다.

 

 

 

 

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

em.persist(memberA);
em.persist(memberB);  //여기까지는 DB에 보내지 않고

transaction.commit()  //이 순간에 한번에 SQL이 전송됨

  • memberA와 memberB에 대한 영속화 요청이 들어오면 JPA는 이 엔티티를 분석하여 적절한 SQL문을 생성한다.
  • 단, 바로 DB에 SQL을 전송하는 것이 아닌 영속성 컨텍스트 내부의 쓰기 지연 SQL 저장소 라는 곳에 차곡차곡 모아둔다.
  • 그리고 트랜잭션 Commit의 순간 쌓여있던 SQL들이 한번에 요청된다.

 

 

변경 감지(더티체크)

find로 가져와서 setter로 그냥 객체 수정 그냥 하면 JPA가 수정을 감지하고 자동 반영해준다. 즉, 다시 persist 안해도 됨.

 

  • Commit 시점에 수정된 엔티티와 그것의 스냅샷(초기 상태를 가지고 있는 일종의 복사본)을 비교하여 바뀐부분이 있는지 확인하고, 이에대한 SQL문을 SQL저장소에 저장한다. 그리고 update 쿼리를 생성하여 전송한다.
  • 엔티티의 삭제도 이런 원리로 작동한다.

 

 

 


플러시란?

  • 영속성 컨텍스트의 변경내용을 DB에 반영하는 것
  • 모아놓은 SQL들을 한번에 실행(트랜잭션과 다름)
  • 영속성 컨텍스트를 비우지 않음
  • 즉, 영속성 컨텍스트와 DB와의 동기화
  • 플러시를 해도 1차캐시는 유지됨

 

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

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

 

JPQL 쿼리 실행시 플러시가 자동으로 실행된다.

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();

왜 자동으로 플러시가 실행되지?

 

persist만으로는 DB까지 반영이 되지 않고 영속성 컨텍스트라는 논리적 공간에만 저장되기에 Commit이 되기 전에 JPQL 쿼리가 요청되면 이에 대한 응답을 해야하기 때문에 현재 영속성 컨텍스트에 있는 것들을 일단 전부 플러시 하는 것. 그래야만 JPQL 쿼리에 대한 응답이 보장된다.

 

 

 


준영속 상태

  • 영속상태였던 엔티티가 영속성 컨텍스트에서 분리된 상태(detached)
  • 영속성 컨텍스트가 제공하는 기능을 사용 못함

 

준영속 상태로 만드는 법

  1. em.detach(entity) - 특정 엔티티만 준영속 상태로 전환
  2. em.clear() - 영속성 컨텍스트를 완전히 초기화
  3. em.close() - 영속성 컨텍스트를 종료

 

 

참고:

'JPA' 카테고리의 다른 글

[JPA] 다양한 연관관계  (2) 2021.07.24
[JPA] 연관관계 매핑  (0) 2021.07.21
[JPA] 엔티티 매핑  (0) 2021.07.19
[JPA] JPA란?  (0) 2021.07.15

+ Recent posts