JPA
3장. 영속성
오초의 기록
2022. 2. 10. 12:00
엔티티 매니저 팩토리와 엔티티 매니저
- 엔티티 매니저 : 엔티티와 관련된 모든 일(저장, 수정, 삭제, 조회)을 처리한다.
- 공장 만들기는 비용이 많이 들지만 공장에서 매니저 생성은 비용이 거의 들지 않는다.
- 엔티티 매니저 팩토리는 여러 스레드가 동시에 접근해도 안전하므로 서로 다른 스레드 간에 공유해도 되지만, 엔티티 매니저는 여러 스레드가 동시에 접근하면 동시성 문제가 발생하므로 스레드 간에 절대로 공유하지 않는다.
- 엔티티 매니저는 데이터베이스 연결이 꼭 필요한 시점까지 커넥션을 얻지 않는다. 보통 트랜잭션을 시작할 때 커넥션을 얻는다.
영속성 컨텍스트(persistence context)란?
- 개념
- 엔티티를 영구 저장하는 환경
- 엔티티 매니저로 영속성을 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.
엔티티의 생명주기

-
- New / transient (비영속) : 영속성 컨텍스트와 전혀 관계가 없는 상태
- Managed (영속) : 영속성 컨텍스트에 저장된 상태
- Detached (준영속) : 영속성 컨텍스트에 저장되었다가 분리된 상태
영속성 컨텍스트의 특징
- 영속성 컨텍스트와 식별자 값
- 영속성 컨텍스트는 엔티티를 식별자 값(@Id로 테이블의 기본키와 매핑한 값)으로 구분한다.
- 영속 상태는 식별자 값이 반드시 있어야 한다.
- 영속성 컨텍스트와 데이터베이스 저장
- JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터 베이스에 반영하는데 이것을 플러쉬(flush)라고 한다.
- 영속성 컨텍스트가 엔티티를 관리할 때 장점
- 1차 캐시 , 동일성 보장 , 트랜잭션을 지원하는 씍 지원 , 변경 감지 , 지연 로딩
엔티티 조회
- 1차캐시
- 영속성 컨텍스트 내부에 캐시를 가지고 있는데 이것을 1차 캐시라고 한다.
- 영속 상태의 엔티티는 모두 이곳에 저장한다.
- @Id로 매핑한 식별자(데이터베이스의 기본키와 매핑)는 Map의 key 역할을 한다.
- Entity 인스턴스는 value 역할을 한다.
- 순서
- 1차 캐시 조회
- 없으면 데이터 베이스 조회
- 조회한 데이터로 엔티티 생성해서 1차 캐시에 저장한다.(영속화)
- 조회한 엔티티를 반환한다.
- 영속성 컨텍스트는 성능상 이점과 엔티티 동일성 보장 (a == b)
- 1차 캐시에 있는 같은 엔티티 인스턴스 반환
엔티티 등록
- 쓰기 지연(transactional write-behind)
- 엔티티 매니저는 트랜잭션을 커밋하기 직전까지 데이터베이스에 엔티티를 저장하지 않고 내부 쿼리 저장소(쓰기 지연 SQL 저장소)에 SQL을 차곡차곡 모아둔다.
- 트랜잭션을 커밋할 때 모아둔 쿼리를 데이터베이스에 보내는데 이것을 트랜잭션을 지원하는 쓰기 지원이라고 한다.
- 커밋 직전에만 SQL을 데이터베이스에만 보내면 문제없다.
- 트랜잭션 커밋 → 엔티티 매니저가 트랜잭션 flush(플러시)
- 쓰기 지연 SQL 저장소에 모인 쿼리를 데이터 베이스에 보낸다.
- flush(플러시) : 영속성 컨텍스트의 변경 내용을 데이터 베이스에 동기화하는 작업
엔티티 수정
- 수정 쿼리를 직접 작성할 때 문제점
- 수정 쿼리가 많아지고, 비즈니스 조직을 분석하기 위해 SQL을 계속 확인해야 한다. 결국 직접적이든 간접적이든 비즈니스 로직이 SQL에 의존하게 된다.
- 변경 감지
- 엔티티의 변경사항을 데이터 베이스에 자동으로 반영하는 기능
- 스냅샷
- JPA는 엔티티를 영속성 컨텍스트에 보관할 때 최초 상태를 복사해서 저장하는 것을 스냅샷이라고 한다.
- 플러시 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다.
- 수정 순서
- 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시(flush)가 호출된다.
- 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾는다.
- 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보낸다.
- 쓰기 지연 저장소의 SQL을 데이터베이스에 보낸다.
- 데이터 베이스 트랜잭션을 커밋한다.
- 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용한다. (비영속, 준영속 x)
- JPA는 엔티티의 모든 필드를 업데이트한다.
엔티티 삭제
- remove()
- 우선 조회 후 삭제
- 삭제 쿼리를 쓰기 지연 SQL 저장소에 등록 후 트랜잭션 커밋해서 플러쉬 호출 시 삭제 데이터베이스에 삭제 쿼리를 전달한다.
- 우선 조회 후 삭제
플러쉬
- 개념
- 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
- 방법
- em.flush() 직접 호출
- 트랜잭션 커밋 시 플러쉬가 자동 호출된다.
- JPQL 쿼리 실행 시 플러쉬가 자동 호출된다.
- 주의점
- 플러시라는 이름으로 인해 영속성 컨텍스트에 보관된 엔티티를 지운다고 생각하면 안 된다. 다시 한번 강조하지만 영속성 컨텍스트의 변경 내용을 데이터 베이스에 동기화하는 것이 플러시다.
준영속
- 개념
- 영속성 컨텍스트가 관리하는 영속상태의 엔티티가 영속성 컨텍스트에서 분리된(detached)된 상태.
- 준영속 상태의 엔티티는 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
- 방법
- em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
- em.clear() : 영속성 컨텍스트를 완전히 초기화
- em.close() : 영속성 컨텍스트를 종료
- 특징
- 거의 비영속 상태에 가깝다.
- 식별자 값을 가지고 있다.
- 지연 로딩할 수 없다.
- 병합(merge)
- 개념
- 준영속 엔티티 → 영속 상태(새로운)
- 병합은 준영속, 비영속인지 신경 쓸 필요가 없다. 식별자 값으로 엔티티를 조회할 수 있으면 불러서 병합하고 조회할 수 없으면 새로 생성해서 병합한다. 따라서 병합은 save or update 기능을 수행한다.
- 개념