[JPA] 영속성 컨텍스트 (Persistence Context)
영속성 컨텍스트(Persistence Context)
영속성이란 지속성이라는 뜻을 가지고 있다. 즉, 데이터를 지속적으로 관리한다라는 목적을 가지고 있다.
JPA(Java Persistence API)에도 Persistence가 들어가는데 그만큼 영속성의 개념이 중요한 개념으로 사용된다.
JPA는 자바에서 사용하는 ORM 기술의 스펙이고 DB와 객체를 매핑한 뒤 영속성 컨텍스트를 통해 관리하게 된다.
영속성 컨텍스트는 논리적인 개념이고 EntityManagerFactory가 생성한 EntityManager를 통해 접근할 수 있다.
엔티티의 생명 주기
영속성 컨텍스트가 관리하는 엔티티의 생명주기는 비영속, 영속, 준영속, 삭제로 관리된다.
비영속 (new/transient)
Member member = new Member();
영속성 컨텍스트와 전혀 관계없는 상태, 즉 객체를 생성하고 영속성 컨텍스트에 저장하지 않은 상태
영속(managed)
Member member = new Member();
em.persist(member);
영속성 컨텍스트에 관리되는 상태. 영속성 컨텍스트에 엔티티를 저장해서 영속성 컨텍스트에 의해 관리되는 상태이다.
persist는 db에 저장하는 게 아니라 엔티티를 영속성 컨텍스트에 저장한다는 뜻이다.
쿼리는 commit 하는 시점에 날리게 된다.
준영속 (detached)
Member member = new Member();
em.persist(member);
em.detach(member);
영속성 컨텍스트에서 분리된 상태
삭제 (removed)
em.remove(member);
삭제된 상태
1차 캐시와 쓰기 지연 SQL 저장소
영속성 컨텍스트 내부에는 1차 캐시와 쓰기 지연 SQL 저장소가 존재한다.
1차 캐시는 엔티티가 영속화되면 엔티티의 상태를 관리하는 곳이다.
1차 캐시를 통한 조회
persist(entity)로 영속된 엔티티를 조회하면 DB에 쿼리를 날리지 않고 우선적으로 1차 캐시를 통해 영속된 엔티티들을 조회한다.
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member = new Member();
member.setMemberId(1L);
System.out.println("before persist");
em.persist(member);
System.out.println("after persist");
em.find(Member.class,member.getMemberId());
System.out.println("before commit");
tx.commit();
System.out.println("after commit");
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
em.close();
emf.close();
}
}
콘솔 창의 결과를 보면 insert 쿼리는 persist시점에 나가지 않고 commit이후에 나가는 것을 알 수 있다.
persist를 호출하면 엔티티는 영속성 컨텍스트에 저장되고 find를 호출하면 JPA는 DB에 select 쿼리를 보내지 않고 1차 캐시에서 엔티티를 조회해서 찾은 엔티티를 반환해준다.
1차 캐시에 없으면 DB에서 조회하고 조회한 엔티티를 영속성 컨텍스트에 올라간다.
public static void main(String[] args) {
//...
tx.begin();
try {
System.out.println("em.find(member1)");
Member findMember1 = em.find(Member.class, 1L);
System.out.println("em.find(member2)");
Member findMember2 = em.find(Member.class, 1L);
tx.commit();
//...
}
처음에 member를 조회해서 1차 캐시에 올리고 다음부턴 1차 캐시에 있는 member를 반환해준다.
쓰기 지연 SQL 저장소
persist를 호출해서 엔티티를 저장했을 때 쿼리는 commit시점에 실행된다고 했는데, 이때 엔티티를 분석해서 insert 쿼리들을 쌓아두는 곳이 쓰기 지연 SQL 저장소라고 한다.
em.persist를 호출해서 member1과 member2를 각각 영속화시키면 1차 캐시에 엔티티가 올라가고 동시에 영속성 컨텍스트에 있는 쓰기 지연 SQL 저장소에 쿼리들이 올라가간다.
쓰기 지연 SQL 저장소에서 관리되는 쿼리들은 commit 시점에 DB에 실행되어 데이터가 반영된다.
변경 감지 (Dirty Checking)
JPA는 쓰기 지연 SQL 저장소와 1차 캐시를 이용해서 엔티티를 수정할 수 있는 변경 감지 기능을 제공한다.
public static void main(String[] args) {
//...
tx.begin();
try {
Member findMember = em.find(Member.class, 1L);
findMember.setName("changeName");
tx.commit();
//...
}
find를 호출해서 엔티티를 조회하면 엔티티가 1차 캐시에 올라간다. commit 시점에 1차 캐시에 올라가 있는 엔티티와 변경된 엔티티를 비교해서 변경된 내용을 쓰기 지연 SQL 저장소에 update 쿼리를 올린 다음 DB에 쿼리를 날려서 데이터를 변경한다.
flush
flush는 영속성 컨텍스트의 변경내용을 DB에 반영한다.
flush가 발생되면 변경 감지가 일어나고 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송하게 된다.
영속성 컨텍스트를 flush 하는 방법
em.flush() - 직접 호출
commit 시점 - 자동으로 호출
JPQL 쿼리 실행 - 자동으로 호출
flush가 호출돼도 1차 캐시는 지워지지 않고 쓰기 지연 SQL 저장소에 쌓인 쿼리들이 DB에 반영되는 것이다.
즉 영속성 컨텍스를 비우지 않고 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 작업이다.
참고 자료 : 김영한님 자바 ORM 표준 JPA 프로그래밍 - 기본편
'Programming > JPA' 카테고리의 다른 글
[JPA] 연관관계 매핑(단방향, 양방향) (0) | 2022.11.14 |
---|