TIL
TIL - 240408 영속성 컨텍스
민경현(John)
2024. 4. 8. 16:19
영속성 컨텍스트(Persistence Context)의 개념
- “영속성”은 사전적인 의미로 ‘오래도록 또는 영원히 계속되는 성질이나 능력’이라고 정의됩니다.
- 애플리케이션과 DB사이에서 영구적으로 저장될 객체를 보관하는 가상의 DB 라고 생각하면 좋을 것 같습니다.
- JPA에서는 테이블과 매핑되는 엔티티 객체 정보를 영속성 컨텍스트를 통해 애플리케션 내에서 오래 지속되도록 보관합니다.
EntityManagerFactory
- EntityManager를 관리하기 위한 객체
- 애플리케이션이 실행될 때 생성되며 하나의 엔티티 매니저 팩토리는 애플리케이션 전체에서 공유되고 서로 다른 스레드가 동시 접근해도 안전합니다.
EntityManager
- 엔티티를 관리하는 역할을 수행(Entity를 저장, 수정, 삭제, 조회 등)하는 클래스
Entity의 생명 주기
- 비영속(new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태
- 엔티티 객체를 생성했지만 아직 영속성 컨텍스트에 저장하지 않은 상태
- 영속(managed) : 영속성 컨텍스트에 저장된 상태
- EntityManger를 통해서 Entity를 영속성 컨텍스트에 저장한 상태를 말하며 영속성 컨텍스트에 의해 관리된다.
- 준영속(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
- 영속성 컨텍스트가 관리하던 영속 상태의 엔티티를 더 이상 관리하지 않으면 준영속 상태가 된다.
- 준영속 상태의 특징
- 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩을 포함한 영속성 컨텍스트가 제공하는 어떠한 기능도 동작하지 않는다.
- 식별자 값을 가지고 있다.
- 준영속 상태의 사용 사례
- 다중 트랜잭션 환경에서의 데이터 전달: 대규모 애플리케이션에서는 종종 여러 트랜잭션 간에 데이터를 전달해야 할 때가 있습니다. 예를 들어, 한 트랜잭션에서 데이터베이스에서 엔티티를 읽고, 이를 다른 트랜잭션으로 전달하여 그 트랜잭션에서 엔티티를 변경할 수 있습니다. 이때 엔티티를 준영속 상태로 만들면, 영속성 컨텍스트가 엔티티를 계속 추적하지 않고, 다른 트랜잭션에서 엔티티를 사용할 수 있습니다.
- 장기 작업 처리: 때로는 영속성 컨텍스트를 오랫동안 유지할 수 없는 장기 작업이 있을 수 있습니다. 예를 들어, 배치 작업이나 긴 처리 프로세스 등이 있을 수 있습니다. 이런 경우에는 영속성 컨텍스트를 유지하지 않고도 엔티티를 사용해야 할 수 있습니다. 엔티티를 준영속 상태로 만들면 영속성 컨텍스트가 엔티티를 계속 추적하지 않으므로 메모리 사용을 줄일 수 있습니다.
- DTO 변환: 때로는 엔티티를 데이터 전송 객체(DTO)로 변환하여 외부 시스템으로 전달해야 할 때가 있습니다. 이때 영속성 컨텍스트가 엔티티를 계속 추적하지 않도록 엔티티를 준영속 상태로 만들면, 불필요한 데이터베이스 쿼리를 피하고 DTO를 더 효율적으로 생성할 수 있습니다.
- 캐싱 제어: 일부 엔티티는 캐시에 저장되거나 캐시에서 로드될 수 있습니다. 이때 엔티티를 준영속 상태로 만들면, 영속성 컨텍스트가 해당 엔티티를 캐시에서 제거하고 메모리를 확보할 수 있습니다.
- 삭제(removed) : 삭제된 상태
- 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한다.
영속성 컨텍스트의 특징
- 영속성 컨텍스트의 식별자 값
- 영속성 컨텍스트는 엔티티를 식별자 값(id 값을 설정해 주지 않으면 오류가 발생한다)으로 구분한다. 따라서 영속 상태는 식별자 값이 반드시 있어야 한다.(영속성 컨테스트가 key-value 형태로 1차 캐시를 관리하는데, 이것을 관리하기 위해서는 PK값이 필수적)
- 영속성 컨텍스트와 데이터 베이스 저장
- JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터 베이스에 반영하는데 이를 Flush라고 한다.
Entity를 영속성 컨텍스트에 보관하여 얻는 장점
- 1차 캐시
- 영속성 컨텍스트에 1차 캐시 존재
- 캐시는 map 형태로 key-value 저장
- key는 DB의 PK, value는 객체
- 1차 캐시는 트랜잭션마다 각자 EntitiyManger를 사용하기 때문에 살아있는 시간이 매우 짧아 성능에 큰 효과는 없다.
- 동일성(identity) 보장
- 트랜잭션을 지원하는 쓰기 지연
- 엔티티 매니저는 트랜잭션을 커밋하기 전까지 DB에 엔티티를 저장하지 않고, 내부 쿼리 저장소에 INSERT 문을 차곡차곡 모아두고 트랜잭션을 커밋할 때 모아둔 SQL문을 DB에 보낸다. 이것을 쓰기 지연이라고 한다.
- 변경 감지
- 영속성 컨텍스트에서 보관하는 데이터에 변경이 일어났는지 확인하고 데이터 변경이 일어났다면 JPA가 UPDATE 쿼리문 날리기 때문에 값 변경 후 update 쿼리문 날릴 코드가 필요가 없습니다.
- 변경 감지는 영속성 컨텍스트가 관리하는 영속상태의 엔티티에만 적용되고 비영속, 준영속 상태의 엔티티에는 적용되지 않는다.
- 지연 로딩
FLUSH
플러시(Flush)는 영속성 컨텍스트에서의 변경 내용을 데이터베이스에 반영하는 과정을 말합니다. 영속성 컨텍스트는 일반적으로 변경된 엔티티를 데이터베이스에 동기화하지 않고도 변경 사항을 추적하고 관리합니다. 이를 통해 여러 변경 작업을 하나의 트랜잭션으로 묶어서 처리할 수 있습니다. 그러나 변경 내용을 데이터베이스에 반영해야 할 때가 있습니다. 이때 플러시가 발생합니다.
플러시하는 방법
- 직접 호출
- entityManager.flush() 직접 호출하여 영속성 컨텍스트를 강제로 플러시 할 수 있습니다. 이 방법은 테스트 또는 다른 프레임워크랑 함께 JPA를 사용할 때를 제외하고 거의 쓰지 않습니다.
- 트랜잭션 커밋 전 플러시 자동 호출
- 일반적으로 영속성 컨텍스트는 트랜잭션이 커밋될 때 변경 내용을 데이터베이스에 반영합니다. 이때 영속성 컨텍스트는 플러시를 수행하여 변경 내용을 데이터베이스에 보냅니다.
- JPQL 쿼리 실행 플러시 자동 호출
- 일부 JPA 구현체는 쿼리를 실행할 때 자동으로 플러시를 수행합니다. 이는 영속성 컨텍스트에 존재하지 않는 변경 내용을 데이터베이스에서 반영해야 할 때 발생합니다.