본문 바로가기

JPA

[JPA] 영속성 컨텍스트

목차

  • 영속성 컨텍스트
  • 엔티티의 생명주기
  • 비영속 상태
  • 영속성 컨텍스트의 이점
  • 플러시
  • 준영속 상태

 

 

JPA에서 가장 중요한 2가지

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

 

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

 

 

 

 

영속성 컨텍스트

  • “엔티티를 영구 저장하는 환경”이라는 뜻 
  • EntityManager.persist(entity);

 

 

엔티티 매니저? 영속성 컨텍스트?

  • 영속성 컨텍스트는 논리적인 개념 
  • 눈에 보이지 않는다. 
  • 엔티티 매니저를 통해서 영속성 컨텍스트에 접근

 

J2SE 환경

  • 엔티티 매니저와 영속성 컨텍스트가 1:1

 

J2EE, 스프링 프레임워크 같은 컨테이너 환경 

  • 엔티티 매니저와 영속성 컨텍스트가 N:1

 

 

엔티티의 생명주기


비영속 (new/transient)

  • 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태 
  • 처음 멤버를 생성했을 때 new Member;

영속 (managed)

  • 영속성 컨텍스트에 관리되는 상태 
  • em.persist(member)
  • 이때, DB에 저장되는 것이 아니다.
  • DB는 이후에 커밋해야 저장됨.


준영속 (detached)

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

삭제 (removed)

  • 삭제된 상태

 

 

 

비영속 상태

//객체를 생성한 상태(비영속) 
Member member = new Member(); 
member.setId("member1"); 
member.setUsername("회원1");

 

 

영속 상태

//객체를 생성한 상태(비영속) 
Member member = new Member(); 
member.setId("member1"); 
member.setUsername(“회원1”);

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);

 

 

 

준영속, 삭제 상태

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

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

 

 

 

영속성 컨텍스트의 이점

  • 1차 캐시 
  • 동일성(identity) 보장 
  • 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind) 
  • 변경 감지(Dirty Checking) 
  • 지연 로딩(Lazy Loading)

 

 

엔티티 조회, 1차 캐시

//엔티티를 생성한 상태(비영속) 
Member member = new Member(); 
member.setId("member1"); 
member.setUsername("회원1");

//엔티티를 영속 
em.persist(member);

 

 

 

1차 캐시에서 조회

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

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

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

 

 

 

데이터베이스에서 조회

Member findMember2 = em.find(Member.class, "member2");
  • 만약member2가 1차 캐시에 없다면 DB를 조회해서 1차 캐시에 올린다. (영속 상태로 변환)

 

 

영속 엔티티의 동일성 보장

 

1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 

트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공

Member a = em.find(Member.class, "member1"); 
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true

 

 

엔티티 등록할 때

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

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

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

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

//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋

 

 

em.persist(memberA);

 

 

em.persist(memberB);

 

persist(entity) 를 하는 경우 JPA 내부에서는 1차 캐시에 저장하고 

동시에 JPA가 entity를 분석해서 쿼리를 생성하고 쓰기 지연 SQL 저장소에 쌓아둔다.

물론, 이때까지 DB에 보관하지 않는다.

 

transaction.commit();

 

쓰기 지연 SQL 저장소에 쌓아둔 쿼리를 flush에 의해서 DB로 날라간다.

flush는 commit( )으로 인해 자동 호출된다.

그리고 실제 DB에 커밋된다.

 

 

엔티티 수정

변경 감지

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

transaction.begin(); // [트랜잭션] 시작

// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");

// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);

//em.update(member) 이런 코드가 있어야 하지 않을까?

transaction.commit(); // [트랜잭션] 커밋

 

 

변경 감지 (Dirty Checking)

 

1차 캐시 안에는 스냅샷이 있다.

스냅샷은 DB에서 값을 읽어온 최초상태를 스냅샷을 남겨둔다.

이후 커밋 시점에 flush가 자동으로 호출된다.

이때 JPA가 엔티티와 스냅샷을 비교한다.

비교를 해보고 바뀐 엔티티가 있다면 UPDATE 쿼리를 쓰기 지연 SQL 저장소에 보관한다.

DB에 저장될 때는 업데이트 쿼리로 동작하고 커밋하게 된다.

 

 

 

엔티티 삭제

//삭제 대상 엔티티 조회 
Member memberA = em.find(Member.class, “memberA");
em.remove(memberA); //엔티티 삭제

 

 

 

플러시

 

영속성 컨텍스트의 변경내용을 데이터베이스에 반영

 

 

플러시 발생

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

 

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

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

 

JPQL 쿼리 실행시 플러시가 자동으로 호출되는 이유

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

//중간에 JPQL 실행, 위에 memberA,B,C는 데이터베이스에서 조회 불가능하기 때문에
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();

 

 

플러시 모드 옵션

 

FlushModeType.AUTO

  • 커밋이나 쿼리를 실행할 때 플러시 (기본값) 

FlushModeType.COMMIT

  • 커밋할 때만 플러시
em.setFlushMode(FlushModeType.COMMIT)

 

 

플러시는!

  • 영속성 컨텍스트를 비우지 않음 
  • 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화 
  • 트랜잭션이라는 작업 단위가 중요 -> 커밋 직전에만 동기화 하면 됨

 

 

준영속 상태

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

 

준영속 상태로 만드는 방법

 

em.detach(entity)

  • 특정 엔티티만 준영속 상태로 전환 

em.clear()

  • 영속성 컨텍스트를 완전히 초기화 

em.close()

  • 영속성 컨텍스트를 종료

'JPA' 카테고리의 다른 글

[JPA] 연관관계 매핑  (0) 2024.06.26
[JPA] 엔티티 매핑  (0) 2024.06.24
[JPA] JPA 동작 확인  (0) 2024.06.23
[JPA] JPA란 뭘까?  (0) 2024.06.23
[JPA] 도메인 분석 설계  (0) 2024.06.20