ORM을 위해 JPA를 쓴다는 것 까지는 이해했다. 하지만, 그 외에도 영속성 컨텍스트라는 중요한 개념이 존재한다.
영속성 컨텍스트는 객체의 상태에 관한 것이다.
객체는 데이터베이스에 저장되었는가, 아닌가로 구분된다기 보단, 생명주기와 관련되어 있다.
영속성 컨텍스트 : 엔티티 자체를 영속화시킨다(DB에 저장된다는 것과는 조금 다른 개념이다)
객체가 영속화되었는가 아닌가를 기준으로 판단되며, 영속화된다는 것은 데이터베이스에 저장된다는 것과 동일한 개념은 아니다.
엔티티의 생명 주기를 통해 영속성에 관해 이야기를 한다.
비영속상태 | 영속성 컨텍스트와는 상관없는 새로운 상태의 객체 |
영속 상태 | 비영속상태의 객체를 영속화 시킨 상태 |
준영속 상태 | 영속성 컨텍스트에서 객체를 빼낸 상태 |
삭제 상태 | 객체 자체를 삭제시킨 상태 |
영속성 컨텍스트의 장점
- 1차 캐시
- 영속성 컨텍스트의 내부에 1차 캐시가 존재한다. (캐시값은 Id, Entity)
- 저장한 이후에 조회를 할 때, 영속성 컨텍스트에서 먼저 1차 캐시를 찾는다.
- 있으면 캐시에 있는 값을 그냥 가져온다.
- if 캐시에 없다면 : 1차 캐시를 찾아보고, 없다면 DB에서 찾은 값을 1차 캐시에 저장시킨 후 반환한다.
- 근데, 애플리케이션 전체가 공유하는 캐시가 아니라, 트랜젝션 하나에서 작동되는 캐시
- 동일성 보장
- DB에는 repeatable read등급의 트랜잭션 격리 수준이 존재한다. 이는 데이터 하나를 기준으로 보았을 때, 설명해야 한다.
- 두개의 트랜잭션이 존재한다. 두 트랜잭션이 한 데이터를 읽어왔을 때, 각 트랜잭션에서 작업이 이루어진다.
- 1번 트랜잭션과 2번 트랜잭션이라고 할 때, 1번 트랜잭션이 데이터를 수정하고 커밋했다.
- 2번 트랜잭션에서 커밋하기 전에 데이터를 읽어오고, 다시 읽어왔을 때 그 데이터는 변한 값이 아닌, 원래 값을 나타낸다.
- 1차 캐시는 이러한 repeatable read등급의 트랜잭션 격리 수준을 DB가 아닌 애플리케이션에서 제공해준다.
- 트랜잭션을 지원하는 쓰기 지연
- JPA에서는 transaction.commit()을 하기 전까지 sql문을 데이터베이스로 보내지 않는다.
- 이는 commit()할 시에 sql문을 한번에 내보낸다.
- 새로운 객체를 생성하는 경우, 이 객체를 1차 캐시에 저장시키면서 sql저장소에 insert sql문을 만들어 저장해둔다.
- sql저장소에 모든 쿼리문이 저장되었다가, commit시에 일괄적으로 보내진다.
- 이를 통해 매 쿼리마다 트랜잭션을 만드는 것이 아닌, 트랜잭션의 조절이 가능하다.
- 변경 감지
- 영속화된 객체의 값을 조회후 수정해 주면 자동으로 이를 영속화시킨다.
- 영속화되어있는 객체가 수정 되었을 시엔, commit때 1차 캐시에서 변경감지를 한다.
- 변경 감지 : ID(객체의 기본키), 객체, 스냅샷(처음 영속성 컨텍스트가 처음 언급되었을 때의 값)을 비교해, 변경이 있을 경우 이를 감지해준다.
- 커밋을 할 때 객체와 스냅샷을 비교하고, 변화가 있을 경우 쓰기지연 sql저장소에 update문을 만들어 저장시켜 DB에 적용시켜준다.
- 지연 로딩
- Lazy, Eager을 통해 지연 로딩과 즉시 로딩으로 구분해 사용할 수 있다.
- Eager(즉시 로딩) : 외래키로 연관관계가 매핑된 객체를 불러올 때 외래키를 기본키로 가지는 객체도 바로 불러오게 한다. 이 때 추가로 가져온 객체는 프록시가 아닌 실제 객체이다.
- Lazy(지연 로딩) : 외래키로 표현시킨 객체는 프록시로 받아오고, 후에 필요할 때가 있다면 그 때 해당 객체를 다시 불러온다.
- 즉시로딩은 어떤 쿼리문으로 나갈지 예측할 수 없기 때문에, 지연 로딩을 통해 하나의 객체만 온전하게 불러오기 더 안전해진다.
영속성 컨텍스트를 사용하는 것으로 이러한 장점을 가질 수 있다.
또한, 위에서 몇번 언급되지만, 커밋을 할 때 변경을 감지하고, 쿼리문을 쓰기 지연 sql 저장소에 모았다가 한번에 보내는 등 데이터베이스로 가기 위한 과정들이 있는데, 이를 플러시(flush)라고 한다.
플러시의 과정은 다음과 같다.
- 변경을 감지한다.
- 수정 엔티티에 대한 update문을 쓰기 지연 SQL 저장소에 등록한다.
- 지연 SQL 저장소의 쿼리문들을 데이터베이스에 전송시킨다. (여기에서 3번, 데이터베이스에 쿼리문들을 보내는 것은 또 3가지 방법이 있다. )
- 엔티티매니저.flush() <- 직접 호출하는 방법
- 트랜잭션.commit() <- 플러시 자동 호출 방법
- JPQL 쿼리 실행 <- 플러시 자동 호출
이러한 영속성 컨텍스트는 몇가지 특이점을 가진다.
- 영속성 컨텍스트는 엔티티 매니저와 1:1로 존재하며, 엔티티 매니저가 close될 때 까지 지워지지 않는다.
- 영속성 컨텍스트의 변경 내용을 DB에 동기화 시킨다.
- 커밋 직전에만 영속성 컨텍스트 동기화를 다 해주면 된다.
- 그러므로, 어디까지 동기화를 하고, 언제 커밋을 할지가 매우 중요하다.
'스프링 공부 > JPA' 카테고리의 다른 글
[JPA] 6. 기본키 매핑 (0) | 2022.10.20 |
---|---|
[JPA] 5. 테이블 자체의 어노테이션에 대하여 (0) | 2022.10.20 |
[JPA] 4. 객체를 데이터베이스와 매핑시키기 - 엔티티 매핑 (0) | 2022.10.20 |
[JPA] 2. 그러면 JPA는 또 무엇일까? (0) | 2022.10.17 |
[JPA] 1. ORM이란 무엇인가? (0) | 2022.10.14 |