영속성 컨텍스트

영속성 컨텍스트

홍찬기
5 min readMay 6, 2021

영속성 컨텍스트란 엔티티를 영구 저장 하는 환경 이라는 뜻이다.
애플리케이션이 데이터베이스에서 꺼내온 객체를 보관하는 역할을 한다. 영속성 컨텍스트는 엔티티 매니저( Entity Manager )를 통해 엔티티를 조회하거나 저장할때 엔티티를 보관하고 관리한다.

엔티티 생명주기

  • 비영속
    영속성 컨택스트와 관계가 없는 새로운 상태
// 엔티티를 생성
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
  • 영속
    엔티티 매니저를 통해 엔티티가 영속성 컨텍스트에 저장되어 관리되고 있는 상태
// 엔티티 매니저를 통해 영속성 컨텍스트에 엔티티를 저장
em.persist(entity);
  • 준영속
    영속성 컨택스트에서 관리되다가 분리된 상태
// 엔티티를 영속성 컨택스트에서 분리
em.detach(entity);
// 영속성 컨텍스트를 비우기
em.clear();
// 영속성 컨택스트를 종료
em.close();
  • 삭제
    영속성 컨택스트에서 삭제된 상태
em.remove(entity)
엔티티 생명주기

영속성 컨택스트의 이점

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

1차캐시

영속성 컨택스트에는 1차캐시가 존재한다. 엔티티를 영속성 컨택스트에 저장하는 순간 1차캐시에 객체가 key(id), value(entity) 값으로 저장된다.

엔티티 매니저가 조회를 할때 먼저 영속성 컨택스트에 있는 1차캐시에서 해당 엔티티를 찾고 엔티티가 존재할 경우 DB에 접근하지 않고 반환한다.
엔티티가 존재하지 않을경우 엔티티 매니저가 em.flush() 할때에 데이터베이스에 접근해서 엔티티를 꺼내오고 해당 엔티티를 1차캐시에 저장한다.

동일성 보장 (Identity)

영속성 컨택스트에서 꺼내온 객체는 동일성이 보장된다.
같은 엔티티를 두번 조회할 경우 두개의 엔티티는 동일한 엔티티이다.
1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.

Member member1 = em.find(Member.class, "member1");
Member member2 = em.find(Member.class, "member1");
System.out.println(member1 == member2) => true

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

트랜잭션 내부에서 persist()가 일어날 때, 엔티티들을 1차 캐시에 저장하고, 쓰기 지연 SQL 저장소 라는 곳에 INSERT 쿼리들을 생성해서 쌓아 놓는다. DB에 바로 넣지 않고 기다린다.
commit() 또는 flush() 를 할때 쓰기지연 SQL 저장소에 저장되어 있는 SQL들을 DB에 보낸다.

EntityManager em = emf.createEntityManager(); 
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작 em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋

변경감지

JPA에서는 엔티티를 업데이트할때 update(), persist() 와 같은 메소드로 영속성 컨택스트에 알려주지 않아도 된다. 이것이 가능한 이유는 변경감지(Dirty Checking) 덕분이다.
엔티티 매니저가 엔티티를 1차캐시에 저장할때 스냅샷도 같이 저장한다. 트랜잭션이 커밋하는 시점에서 엔티티와 스냅샷을 비교해 변경사항이 있으면 Update SQL 을 알아서 생성해서 쓰기지연 저장소에 SQL을 저장한다.

값만 바뀌었는데 업데이트 쿼리가 발생하는 이유는 JPA가 엔티티 관리를하고 트랜잭션이 커밋하는 시점에서 변경된 사항을 체크를하고 변경이 되었으면 업데이트 쿼리를 만들어서 요청하고 트랜잭션이 커밋이됨 
Member findMember = em.find(Member.class, 1L); findMember.setName(“HelloJPA”);

플러시

플러시는 영속성 컨택스트의 변경내용을 데이터베이스에 반영한다.
트랜잭션 커밋시점에서 플러시가 발생하는데 이때 쓰기지연 저장소에 쌓여있는 SQL문들을 데이터베이스에 전송한다.

플러시는 영속성 컨택스트를 비우는것이 아니다. 쌓여있는 SQL문들 데이터베이스에 전송하는것 뿐이다.

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

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

Reference

자바 ORM 표준 JPA 프로그래밍

--

--