[JPA] 영속성 컨텍스트를 왜 사용하는가?
Intro
앞 게시물에서 영속성 컨텍스트를 정리했다. 정리를 하면서 왜 영속성 컨텍스트라는 공간을 사용하는 가? 라는 생각이 들었고 공부를하였기에 이를 정리한다.
영속성 컨텍스트란
영속성 컨텍스트 사용 이유(이점)
- 첫째로는 1차 캐시 사용이다. 영속성 컨텍스트에 있는 1차 캐시라는 공간이 있는데 영속화 된 엔티티가 존재할 경우 DB에 쿼리를 보내지 않고도 엔티티를 가져올 수 있기에 DB를 거치지 않고도 엔티티를 얻을 수 있는 장점이 있다.
- 두번째로는 동일성 보장이다. 1차 캐시 공간에 Id(식별자 값)가 같은 엔티티에 대해서 동일성을 보장한다.
- 세번째로는 변경감지(Dirty Checking)이다. 1차 캐시에 들어온 데이터에서 변경이 됐을 때 쓰기 지연 SQL 저장소에 쌓아둔 후 flush를 하면서 변경된 내용을 반영한다는 장점이 있다.
- 마지막으로는 쓰기지연(Dirty Checking)이다. 트랜잭션 커밋하기 전까지 SQL을 DB에 보내지 않고 쓰기 지연 SQL 저장소에 보관했다가 한 번에 커밋했을 때 보낼 수 있다.
영속성 컨텍스트 이점
1. 1차 캐시
1차 캐시란, 영속성 컨텍스트에 있는 공간으로 영속화 된 엔티티를 저장하는 공간이다.
- 엔티티가 영속화 될 때 key : value 형태로 저장된다.
- key에는 @id로 선언한 필드값이 저장되고, value에는 Entity가 저장되어 @Id로 선언한 필드값으로 1차 캐시 내에서 구분한다.
-
1차 캐시는 조회 할 때 DB로 바로 접근하지 않고 1차 캐시에서 먼저 조회하여 해당 엔티티를 먼저 찾을 수 있는 공간이다.
- 영속화 된 엔티티가 있을 경우 DB를 거치지 않고 엔티티를 반환하지만, 없는 경우에는 DB를 검색하고 해당 엔티티를 1차 캐시에 저장하고 반환한다.
2. 동일성 보장
- 동일성이란, 자바에서 두 객체가 갖고 있는 값이 일치하고 메모리 상에서 같은 주소에 위치할 때의 성질을 의미한다.
- 1차 캐시에서 @Id가 같은 엔티티를 반복하여 호출하여도 같은 인스턴스를 반환하여 동일성을 보장할 수 있다.
1 2 3 4 5 6
// Id가 1인 User 엔티티를 조회 User user1 = em.find(User.class, 1L); User user2 = em.find(User.class, 1L); System.out.println(user1 == user2);//true가 반환
- 1차 캐시로 반복 가능한 읽기(REPETABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.
3. Dirty Checking(변경 감지)
- JPA에서 별도의 update 코드가 필요하지 않은데 Dirty Checking을 통해 변경된 내용을 DB에 반영하기 때문이다.
- 원리
- 1차 캐시에 저장될 때(영속화 될 때) 스냅샷을 찍어두게 되는데, 엔티티가 변경되면 변경된 내용을 스냅샷과 비교하여 감지한 후 트랜잭션 커밋 시점에 변경된 내용을 DB에 반영하는 것이다.
- 따라서, 커밋하기 전에 원상복구 시키면 update 쿼리가 실행되지 않는다.
4. flush(플러시)
flush(플러시)란, 영속성 컨텍스트와 데이터베이스를 동기화하는 것을 의미한다.
- 트랜잭션이 커밋되는 순간에 flush가 발생한다. flush가 발생하게 되면 쓰기 지연 SQL 저장소에 있는 SQL 등이 데이터베이스에 전달되어 변경된 내용을 반영하여 DB와 영속성 컨텍스트를 동기화하는 것이다.
- 플러시를 한다고 해서 영속성 컨텍스트를 비우는 것이 아니다.
- flush 방법
- 트랜잭션 커밋(자동 flush)
- JPQL 쿼리 실행(자동 flush)
- em.flush(직접 flush, EntityManager)
- 순서
- Dirty Checking(변경감지)
- 변경된 내용 존재할 경우 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송하여 동기화함.
5. 쓰기 지연
- em.persist(user) 를 통해 영속화 했을 때 Insert 쿼리문을 DB에 바로 보내는 것이 아니라, 1차 캐시 공간에 저장하면서 동시에 쓰기 지연 SQL 저장소에 쌓아둔다.
- 이후 transaction.commit()을 하게 되면 쓰기 지연 SQL에 있는 INSERT 쿼리를 DB에 보낸다.
요약(commit ~ flush)
- commit 하는 시점에 Dirty Checking(변경 감지)를 하여 변경 내용을 쓰기 지연 SQL 저장소에 저장한다.
- 그리고 flush가 발생하는데, 변경된 내용을 DB와 동기화하는 작업을 진행한다.
결론
영속성 컨텍스트를 사용했을 때 1차 캐시에 저장하여 쓰기 지연을 통해 바로바로 DB에 커밋되지 않도록, 커밋 전까지 변경을 자유롭게 할 수 있다는 이점을 갖고 있다.
그리고 변경되는 내용을 스냅샷을 통해 Dirty Checking이 이뤄진다.