JPA 영속성 컨텍스트란?

배태현·2021년 5월 2일
6

JPA

목록 보기
1/7
post-thumbnail

시작하기 앞서 저의 글에대한 피드백이나 지적은 언제나 환영입니다 😊

영속성 컨텍스트란?

  • 엔티티를 영구저장하는 환경 이라는 뜻
  • 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다.
  • 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

em.persist(member);

엔티티 매니저를 사용해 member(회원엔티티)를 영속성 컨텍스트에 저장한다는 의미이다.

영속성 컨텍스트의 특징

  • 엔티티 매니저를 생성할 때 하나 만들어진다.
  • 엔티티 매니저를 통해 영속성 컨텍스트에 접근하고 관리할 수 있다.

엔티티의 생명주기

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

비영속

: 객체를 생성한 상태, 아직 영속성 컨텍스트에 저장하지 않은 상태.

Member member = new Member();

영속

: 객체를 저장한 상태, 영속성 컨텍스트에 의해 관리된다

em.persist(member);

준영속

: 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태

em.detach(member);
  • 영속 -> 준영속
  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)
  • 영속성 컨텍스트가 제공하는 기능을 사용하지 못함
    : (1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩을 포함한 영속성 컨텍스트가 제공하는 어떠한 기능도 동작하지 않음.)
//특정 엔티티만 준영속 상태로 전환 -> 영속성 컨텍스트에서 분리하는 것
em.detach(member);

//영속성 컨텍스트를 완전히 초기화 -> 관리되던 엔티티는 준영속 상태가 됨
em.clear();

//영속성 컨텍스트를 종료 -> 관리되던 엔티티는 준영속 상태가 됨
em.close();

삭제

: 객체를 삭제한 상태

em.remove(member);
  • DB에서도 삭제가 된다.

영속성 컨텍스트의 이점

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

엔티티 조회, 1차캐시

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

데이터베이스에서 조회

Member findMember2 = em.find(Member.class, "member2");

영속 엔티티의 동일성 보장

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

변수들은 서로 다른 메모리에서 생성되기 때문에 동일성 비교를 했을 때 동일성이 보장되지 않지만
영속된 엔티티들은 영속성컨텍스트가 관리해주므로 1차캐시에 있는 같은 엔티티를 반환받았기 때문에 같은 인스턴스 이므로 동일성이 보장된다

동일성 비교 : 실제 인스턴스가 같다 ==을 사용해 비교한다.

동등성 비교 : 실제 인스턴스는 다를 수 있지만 인스턴스가 가지고 있는 값이 같다.
-> equals()메소드를 사용하여 비교한다

엔티티 등록, 트랜잭션을 지원하는 쓰기지연

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

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

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

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



즉, 내부 SQL저장소에 쿼리문들을 모아뒀다가 트랜잭션이 커밋될때 모아둔 쿼리문을 DB에 보낸다.
: 이것을 트랜잭션을 지원하는 쓰기지연이라고 한다.

엔티티 수정, 변경감지(Dirty Checking)

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(); // [트랜잭션] 커밋

:위 처럼 update같은 코드가 있어야 하지 않을까?
결과부터 말하자면 없어도 된다.
영속성 컨텍스트가 관리해주는 엔티티는
플러시 시점에 1차캐시(스냅샷)와 비교해 변경된 점이 있다면 자동으로 update쿼리문을 날려준다.

엔티티 삭제

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

em.remove(memberA); //엔티티 삭제, DB에서도 삭제됨

플러시

  • 영속성 컨텍스트의 변경내용을 DB에 반영하는 것

플러시 발생

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

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

  • em.flush()로 직접 호출하는 방법
  • 트랜잭션 커밋시점에 플러시가 자동으로 호출된다.
  • 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();

: 플러시 모드 옵션을 보면 알 수 있다.

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

플러시는?

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

글을 마치며.

JPA에서는 가장 중요한 부분이 2개 있다고 합니다.
바로 영속성관리와 연관관계매핑 입니다.
그 중 중요한 한가지 영속성관리에 대해 정리해봤습니다.
영속성 컨텍스트의 내부 동작 방식을 이해하며 굉장히 흥미로웠습니다.
그리고 이 내부동작방식을 이해하지 못하면 JPA를 활용한 개발은 암기가 될 것 같다고 생각이 들었습니다.
긴 글 읽어주셔서 감사합니다.😊

profile
일상의 불편함을 기술로 해결 할 방법을 고안합니다.

0개의 댓글