JAVA/Application

[JPA] Entity는 영속성 컨텍스트에 저장/조회한다!

히어로맛쿠키 2023. 1. 25. 01:53

🪄 Intro

이 포스팅은 단일 트랜잭션 실습 중 생긴 의문점을 해결한다.

영속성 컨텍스트의 '1차캐시' 개념을 알게 되었다.

 

 


🚨 상황

- Spring JPA를 통한 @Transactional 테스트코드 실습중!

 

↓ 내 테스트코드

Entity의 속성값 "YEEUN KIM"을 "SIKYONG SUNG"으로 변경하는 코드

@Test
@Transactional  // 영속성 컨텍스트 안에서 관리한다.
void UPDATE_TEST() {
    // Given : 엔티티를 영속성 컨텍스트에 저장
    CustomerEntity customer = new CustomerEntity();
    customer.setId(1L);
    customer.setFirstName("YEEUN");
    customer.setLastName("KIM");
    repository.save(customer);

    // When : 엔티티 값 변경
    CustomerEntity entity = repository.findById(1L).get();
    log.info("{} {}", entity.getFirstName(), entity.getLastName()); // 변경 전 값이 찍힌다
    entity.setFirstName("SIKYONG");
    entity.setLastName("SUNG");

    CustomerEntity updated = repository.findById(1L).get();
    log.info("{} {}", updated.getFirstName(), updated.getLastName()); // 변경 후 값이 찍힌다
}

 

 

🚨의문 & 상황검토

 

아래 로그를 보고 의문에 휩싸였다.

 

중간에 값을 변경하고 로그를 찍었더니 변경한 값(SIKYONG SUNG)이 나왔다. 이건 commit된 값이 아니지 않나? 왜 변경한 값이 찍히지?

 

① commit 안한 값을 읽었어??

- 엔티티 값을 "SIKYONG SUNG"으로 업데이트했다.

- 그러나 @Transaction 메서드가 아직 완료되지 않았으므로 "SIKYONG SUNG"은 commit되지 않은 값이 아닌가? 그런데 어째서 이게 read되는 걸까?

 

 

② 현재 격리수준은??

내 상황을 검토해보았다.

- 나는 격리수준이 read commited인 H2 DB를 사용하고 있다.

- @Transactional을 통해 영속성 컨텍스트에서 관리되고 있다.

- @Transactional의 격리 수준은 default이다. 즉 사용하는 DB의 격리수준을 따른다.

- 따라서 현재 격리수준은 read commited 이다.

 

 

위 의문은 "영속성 컨텍스트"를 이해하면 해결된다!

 

 

 

🍒 해답

일단 의문에 코멘트를 달자면.. 

 

① commit 안한 값을 읽었어??

=> 맞다.

=> 근데 애초에 select할 때 DB에서 값을 읽는 게 아니다.

=> "영속성 컨텍스트의 1차캐시"에서 읽어옴을 알아야 한다.

 

② 현재 격리수준은??

=> (언급했듯) read commited

=> 단일 트랜잭션 실습일뿐만 아니라, 어차피 트랜잭션 내에서 Entity 생성하고 업뎃하고 다 하고 있다. 그래서 이 문제에서 격리수준은 딱히 신경 쓸 필요 없어보인다.

 

 

 

그래서 해답은 "영속성 컨텍스트의 1차 캐시"를 이해하는 것에 있다.

 

∨ 조회할 때는 DB가 아닌 1차캐시에서 먼저 조회한다.

- 조회시 1차캐시에 객체가 있는지 확인(PK로 확인)하고, 있다면 가져오기

- 1차캐시에 없다면 2차캐시에서 조회한다.

- 2차캐시에도 없다면 DB에서 조회하여 객체의 스냅샷를 가져온다. 그 스냅샷을 1차2차캐시에 저장한다.

 

 저장하거나 수정할 때도 1차캐시에 변경사항을 저장한다.

 

∨ 참고) 위와 같은 쿼리들은 '내부의 쿼리 저장소'에 일단 저장되고, 최종적으로 트랜잭션 커밋시 한번에 수행되고 나서 DB에 반영된다. 참고로 이는 영속성 컨텍스트의 '쓰기 지연' 특징이다. 여기서는 다루지 않겠음.

 

 

 

🍒 코드 한줄한줄을 영속성 컨텍스트 기반으로 설명해보자.

참고) 아래에서 '영속성 컨텍스트에서 찾는다'라는 표현은,

영속성 컨텍스트의 1차캐시 or 2차 캐시에서 찾는다는 말이다.

 

 

▼ Entity를 만든다. 아직 비영속 상태의 Entity다.

CustomerEntity customer = new CustomerEntity();
customer.setId(1L);
customer.setFirstName("YEEUN");
customer.setLastName("KIM");

 

▼ 영속성 컨텍스트에 Entity를 save한다. 영속 상태의 Entity다.

repository.save(customer);
더보기

Entity를 save할 때 영속성 컨텍스트에서 관리된다.

참고로 save(Entity)할 때 Entity의 PK가 있으면 save, 없으면 update 쿼리가 나간다

 

▼ findById(PK)를 통하여 영속성 컨텍스트에서 해당 PK를 가진 객체를 찾아 가져온다.

CustomerEntity entity = repository.findById(1L).get();

 

▼ Entity를 update하게 되면, 변경 사항이 영속성 컨텍스트에 반영된다.

entity.setFirstName("SIKYONG");
entity.setLastName("SUNG");

 

▼ findById(PK)를 통하여 영속성 컨텍스트에서 해당 PK를 가진 객체를 가져온다.

CustomerEntity updated = repository.findById(1L).get();

 

 

아핫 이제 이해가 된다.

영속성 컨텍스트에 저장한다는 그 개념!

 

 


🍒 이로써 의문 해결!

 

처음에는 "왜 commit되지 않은 값을 읽은거야! read commited잖아!!" 라고 생각하면서 아래 로그를 의아해했지만 이제는 이해할 수 있다. commit과 상관 없다.

Entity는 영속 상태로서, 영속성 컨텍스트의 1차캐시에 반영되고, 1차캐시에서 읽어오기 때문이다!

 

 

 


공부에 도움이 된 글:

https://blog.neonkid.xyz/233

 

[Spring] JPA 영속성 컨텍스트 구조로 보는 이점

지난 포스트에서 JPA의 영속성 컨텍스트와 생명 주기에 대해 알아봤습니다. 간단하게 영속성 컨텍스트의 생명 주기의 관계를 통해서 Java로 구현된 객체가 어떻게 DB로 적재되고 삭제되는지를 알

blog.neonkid.xyz

https://www.blog.ecsimsw.com/entry/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-1%EC%B0%A8-%EC%BA%90%EC%8B%9C-%EC%93%B0%EA%B8%B0-%EC%A7%80%EC%97%B0

 

JPA / 영속성 컨텍스트 / 1차 캐시 / 쓰기 지연

"T아카데미 / JPA 프로그래밍 기본기 다지기 - 김영한 " 강좌를 듣고 정리한 글입니다. 영속성 컨텍스트를 영속성 컨텍스로 관리하면 어떤 이점을 갖을까 1. 1차 캐시 / 엔티티 동일성 보장 2. 쓰기

www.blog.ecsimsw.com