반응형
1.영속성 컨텍스트란?
- 먼저 엔티티(Entity)의 의미를 먼저 간단하게 알고 넘어가면 엔티티는 다음과 같다.
'업무에 필요하고 유용한 정보를 저장, 관리하기 위한 개념'으로, 실체, 객체라는 의미이다.
예) 엔티티-직원 / 속성- 주소, 사번, 휴대전화번호 등등 - 영속성 컨텍스트(Persistence context)는 '엔티티를 영구히 저장하는 환경' 이라는 의미를 가집니다.
어플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 DB 같은 역할을 하게된다. - 영속성 컨텍스트는 엔티티 매니저를 생성할 때 만들어지며 엔티티 매너저를 통해 접근 관리된다.
2. 엔티티 매니저란?
- 엔티티를 영속성 컨텍스트에 저장, 보관 등의 관리를 하는 역할을 한다.
- 엔티티 매니저에 엔티티를 저장하거나 조회하게 되면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관, 관리한다.
- 엔티티 매니저는 엔티티 매니저 팩토리를 통해 생성된다.
- 생성하는 비용이 거의 들지 않고 여러 스레드가 동시에 접근하면 동시성 문제가 발생하므로 스레드간에 절대 공유 불가하다.
- 데이터베이스 연결이 필요한 시점까지는 커넥션을 얻지 않는다.
3.엔티티 매니저 패토리란?
- 엔티티 매니저를 만드는 공장이라고 생각하면된다.
- 생성 비용이 상당히 크기 때문에 하나만 만들어서 어플리케이션 전체에서 공유하도록 설계되어 있다.
- 전체에서 공유하도록 설계되어 여러 스레드가 동시에 접근해도 안전하므로 다른 스레드간에 공유해도 된다.
4. 엔티티 생명주기
- 엔티티 생명주기는 비영속(New), 영속(Managed), 준영속(Detached), 삭제(Removed) 이렇게 4가지 가집니다.
- 비영속(New)
- 영속성 컨텍스트와 관련이 없는 상태, DB와 관련이 없는 순수한 객체 상태를 의미.
- User라는 엔티티가 정의 되어 있을 때 new 키워드로 객체를 생성한 상태
예 ) User user = new User()
- 영속(Managed)
- 엔티티 매니저가 관리하는 영속성 컨텍스트에 엔티티가 저장된 상태를 의미
- persist()메소드 호출, find(), JPQL, QueryDSL로 엔티티를 조회하여 영속성 컨텍스트에 저장
- 준영속(Detached)
- 영속성 컨텍스트에 저장되었다가 분리된 상태로 엔티티 매너지가 더 이상 관리하지 않는 상태를 의미.
- 준영속 상태가 되면 1차캐시, 쓰기지연 SQL저정소까지 모든 정보가 제거된다.
- detach()
- 특정 엔티티를 준영속 상태로 만든다.
- em.detach(order); - clear()
- 영속성 컨텍스트를 초기화해 영속성 컨텍스트에 존재하는 모든 엔티티를 준영속 상태로 만든다.
- 초기화 되었기에 새로 만든 영속성 컨텍스트 상태와 동일하다.
- em.clear(); - close()
- 영속성 컨텍스트를 종료합니다.
- em.close(); - merge()
- 준영속 상태의 엔티티를 영속성 컨텍스트에서 관리되는 영속상태로 만든다.
- 삭제(removed)
- 특정 엔티티를 영속성 컨텍스트에서 삭제한다.
- 엔티티 등록과 비슷하게 삭제쿼리를 '쓰기지연 SQL저장소'에 등록한다.
(영속 컨텍스트 내 '1차 캐시'에서는 제거됨)
- em.remove(order);
5. 영속성 컨텍스트의 구조
- 1차 캐시 저장소 : 영속성 컨테그스가 관리하는 엔티티 정보를 보관하는 장소로 1차캐시에 저장되면 해당 엔티티는 영속상태가 된다. 단, 영속 상태는 DB에 저장된 상태가 아니다.
- 쿼리문 저장소 : 필요한 쿼리문을 보관해두게 되는데 나중에 저장된 쿼리문을 DB로 접근하는 행위를 'flush()' 메소드를 통해 수행하게 됩니다.
6. 영속성 컨텍스트의 특징
- 1차 캐시
- 엔티티를 저장하거나 조회하는 경우에 1차 캐시에 저장되며 캐시에 저장된 데이터를 가져오게 된다.
- 만약, '1차 캐시'내에 데이터가 없는 경우에는 DB에서 엔티티를 가져오기 위해 Select 쿼리를 수행하여 가져오게 됩니다.
즉, 캐시에 데이터가 있든 없든 find()의 반환값은 캐시에 저장된 엔티티가 반환됩니다.
- 동일성 보장
- JPA의 경우 '1차 캐시'에서 엔티티를 조회하므로 동일한 객체를 반환하게 됩니다.
- 트랜잭션을 지원하는 쓰기지연(Transaction write behind)
- 쓰기지연 SQL 저장소에 있는 모아진 쿼리를 엔티티 매니저가 commit() 메소드를 호출하면 한꺼번에 DB로 보내게 되는 것을 말합니다.
- 쿼리를 모아 두었다가 한 번에 DB로 보내기 때문에 불필요한 리소스 낭비가 없어 성능을 높일 수 있습니다.
- commit() 메소드의 경우 내부에 flush 작업이 포함되어있습니다.
- 변경 감지(Dirty checking)
- 1차 캐시에 존재하는 엔티티에 대해 setter를 이용하여 값을 수정하면 자동으로 UPDATE가 발생합니다.
- 엔티티 매니저에는 update는 없는데 그 이유가 변경감지 기능을 통해 Update 하기 때문입니다.
- JPA는 엔티티를 1차 캐시에 저장할 때, 최초 상태를 복사해서 저장하는데 이를 스냅샷 기능이라고 합니다.
flush() 메소드 호출 후 스냅샷과 실제 엔티티를 비교하고 복사본과 실제 넘어온 엔티티가 다르다면 변경을 감지하고 Update문을 생성하여 쿼리문을 실행하게 됩니다. - Update의 경우 기본 설정으로 전체 필드에 대해 전부 업데이트가 되지만, 변경된 필드만 Update를 위해서는 엔티티 정의시 @DynamicUpdate 어노테이션을 추가해 준다.
- 플러시(flush)
- 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
- 즉, 쓰기지연 SQL 저장소에 쌓여 있던 SQL문들이 DB에 실행을 하는 단계를 말하며 이때 실제로 DB에 반영되지는 않는다.
실제 DB에 반영되는 시점은 commit 단계이다. - 플러시가 발생하더라도 1차 캐시는 삭제되지 않는다.
- 플러시의 동작 과정
- 변경 감지 - Dirty Checking
- 수정된 Entity를 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쌓여진 Query를 DB에 전송 실행한다(Insert, Update, Delete)
- 플러시를 호출하는 방법
- 엔티티 매니저를 통하여 직접 호출
- em.flush(); - 트랜잭션 커밋 : 트랜잭션 commit 시 내부에 flush 메소드를 수행하도록 되어있다
- tx.commit(); - JPQL 실행
- JPQL이 실행될 때 flush가 발생하는 이유
아래와 같이 order1, 2,3 을 영속화(persist) 상태에서는 쓰기지연 SQL 저장소에 쿼리만 생성된 상태이다.
JPQL로 SELECT 쿼리를 날리려고 하면 저장되어 있는 값이 없어 문제가 발생할 수 있다.
이를 방지하고자 JPQL 실행 전 무조건 flush()로 DB와의 싱크를 맞춘 후 JPQL 쿼리를 실행하도록 설정되어 있다.
- 엔티티 매니저를 통하여 직접 호출
- 플러시 모드 옵션
- 엔티티 매니저에 플러시 모드를 직정 지정이 가능한데 javax.persistence.FlushModeType을 사용하면된다
- FlushModeType.COMMIT : 커밋할 때만 플러시
em.setFlushMode(FlushModeType.COMMIT); - FlushModeType.Auto : 커밋이나 쿼리를 실행할 때 플러시(default)
em.setFlushMode(FlushModeType.Auto);
반응형
'Backend > Spring' 카테고리의 다른 글
[JPA] N+1 문제 (0) | 2022.02.28 |
---|---|
JPA 즉시로딩(Eager Loading)과 지연로딩(Lazy Loading)? (0) | 2022.02.24 |
JPA 프록시란? (0) | 2022.02.24 |
Spring Data JPA 란? (0) | 2022.02.20 |
IOC(Inversion of Colntrol) 컨테이너 (0) | 2017.06.27 |