JPA : 영속성 컨텍스트와 엔티티의 동일성
- Spring/JPA
- 2022. 2. 15.
이 게시글은 자바 ORM 표준 JPA 프로그래밍 책을 보고 공부한 내용을 정리한 글입니다.
영속성 컨텍스트와 1차 캐시
영속성 컨텍스트는 내부적으로 1차 캐시를 가지고 있다. 이 1차 캐시는 DB에서 가져온 엔티티를 영속화 해두는 저장소 역할을 한다. 이 저장소는 스냅샷을 만들어 더티 체킹도 하지만, PK 값으로 엔티티를 관리하기 때문에 엔티티의 비교에도 아주 유용한 기능을 제공한다.
영속성 컨텍스트가 1개 일 때, 엔티티 비교.
영속성 컨텍스트가 1개일 때 엔티티를 비교하면 어떻게 될까? OSIV 환경을 가정하고 비교해보자. 먼저 빈 영속성 컨텍스트에 PK가 1인 엔티티를 DB에서 영속화했다. 그럼 영속성 컨텍스트의 1차 캐시에 이 엔티티는 영속화된다. 그리고 이 엔티티를 A라는 변수에 참조하도록 한다.
다시 한번 PK가 1인 엔티티를 조회해서 B에 저장하는 상황을 가정하자. JPA는 DB에 조회 쿼리를 날리기 전에 영속성 컨텍스트의 1차 캐시를 살펴본다. 이 때, 1차 캐시에 영속화된 엔티티가 존재하기 때문에 이 엔티티가 B에 참조하도록 된다.
위와 같은 상황이면 A와 B는 동일한 참조값을 가진다. 영속성 컨텍스트의 1차 캐시에 영속화 된 동일한 엔티티를 가리키기 때문이다. 따라서 영속성 컨텍스트가 1개 일 때, 동일한 엔티티를 비교하면 아래 내용을 만족한다.
- A == B
- 같은 주소를 가지기 때문에 A == B를 만족한다.
- A.equlas(B)
- 같은 주소를 가지기 때문에 2항도 만족한다.
- @Id
- 같은 엔티티기 때문에 DB에서도 동일성이 보장된다.
영속성 컨텍스트가 2개 일 때, 엔티티 비교.
영속성 컨텍스트가 1개 일 때, 동일한 엔티티는 세 항목을 만족했다. 영속성 컨텍스트가 2개인 경우에는 어떻게 바뀔까? 영속성 컨텍스트가 2개인 경우 트랜잭션마다 영속성 컨텍스트 생성 전략을 살펴보면 된다. 먼저 아래와 같이 동작했다고 해보자.
- 트랜잭션 1이 시작해서 PK = 1 인 엔티티를 조회해서 영속화하고, 그 엔티티를 A에 참조하도록 했다.
- 트랜잭션 1가 종료하며 영속성 컨텍스트가 종료되고, 엔티티는 준영속화된다.
- 트랜잭션 2가 시작해서 PK = 1 인 엔티티를 조회해서 영속화하고, 그 엔티티를 B에 참조하도록 했다.
- 트랜잭션 2가 종료하며 영속성 컨텍스트가 종료되고, 엔티티는 준영속화된다.
위 경우에 A와 B는 서로 다른 객체를 참조한다. 왜냐하면 2의 과정에서 영속성 컨텍스트가 종료되었기 때문에 3을 할 때, 영속성 컨텍스트의 1차 캐시를 살펴보는 것이 아니라 다시 한번 DB에서 엔티티를 불러와서 다른 영속성 컨텍스트에 저장하기 때문이다. 따라서 A와 B는 같은 엔티티를 조회했지만, 서로 다른 객체를 가리키게 된다.
- A != B
- 서로 다른 객체를 가리키기 때문에 == 비교는 실패한다.
- A.equlas(B)
- 같은 엔티티를 가리키기 때문에 equals는 성공한다. 대신 이 때, 어떤 컬럼이 equals의 기준이 될 지 미리 결정해야한다.
- @Id
- 같은 엔티티기 때문에 DB에서도 동일성이 보장된다.
정리
- 영속성 컨텍스트가 1개 일 때, 같은 엔티티를 비교하게 되면 ==, equals, @Id 모두 동일성을 보장한다.
- 영속성 컨텍스트가 2개 이상일 때, 같은 엔티티를 비교하게 되면 == 비교는 실패한다. equals와 @Id는 동일성을 보장하지만, equals는 개발자가 직접 구현해야한다.
'Spring > JPA' 카테고리의 다른 글
JPA : 일대다 Join (Collection Fetch Join) + Distinct (0) | 2022.02.21 |
---|---|
JPA : 일대다 연관관계에서 테이블 생성 (0) | 2022.02.21 |
JPA : 동시성 문제 + OSIV (0) | 2022.02.15 |
JPA : 읽기 전용 성능 최적화 (0) | 2022.02.07 |
JPA : Pageable 객체를 이용한 페이징 (0) | 2022.02.06 |