본문 바로가기
혼자 공부하는 것들/JPA

[지구최강 JPA 스터디] 1주차 영속성 컨텍스트란?

by applepick 2022. 6. 30.
반응형

회사에서 동기들과 JPA를 함께 스터디를 하기로 했습니다.

https://www.inflearn.com/course/ORM-JPA-Basic/

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

회사가 인프런 강의를 지원해줘서 위에 강의를 베이스로 스터디를 진행했습니다.

 

1주차 정리

EntityManagerFactory가 EntityManager를 만들어줍니다. EntityManager를 통해서 영속성 컨텍스트에 접근합니다.

스프링 프레임워크 같은 컨테이너 환경에서 EntityManager와 영속성 콘텍스트는 N대1 관계입니다.

Entity의 생명주기는?

생명주기는 4가지로 나눠집니다.

비영속 => 영속성 컨텍스트와 전혀 관계없는 새로운 상태

AppFormEntry appFormEntry  = new AppFormEntry(100L);
Long Id = appFormEntry.getId();

이런 식으로 순수하게 자바 객체로만 이루어져 있다면 비영속입니다.

 

영속 => 영속성 컨텍스트에 관리되는 상태

AppFormEntry appFormEntry  = new AppFormEntry();
appFormEntry.setId(100L);

em.persist(appFormEntry);

위에 코드처럼 엔티티가 영속성 컨텍스트에 관리되고 있는 상태를 말합니다.

 

준영속 => 영속성 컨텍스트에 저장되어있다가 분리된 상태

em.detach(appFormEntry);

detach를 통해 appFormEntry 객체를 영속성 컨텍스트에서 분리된 상태를 말합니다.

 

삭제 => 객체를 삭제한 상태

em.remove(appFormEntry);

remove를 통해 삭제한 상태를 말합니다.

 

영속성 컨텍스트의 장점

  • 1차 캐시 지원
  • 동일성(identity) 보장
  • 트랜잭션을 지원하는 쓰기 지연
  • 변경 감지(Dirty Checking)
  • 지연 로딩(Lazy Loading)

1차 캐시 

AppFormEntry appFormEntry  = new AppFormEntry();
appFormEntry.setId(100L);

em.persist(appFormEntry);

AppFormEntry findAppFormEntry = em.find(AppFormEntry.class, 100L);
AppFormEntry findAppFormEntry2 = em.find(AppFormEntry.class, 100L);

위와 같은 코드가 있다고 보자면, findAppFormEntry는 직접 DB에서 꺼내온 객체이고, findAppFormEntry2는 영속성 컨텍스트 안에 1차 캐시에서 가져온 객체입니다. 이렇게 보면 이점이 있지만, 1차 캐시는 하나의 트랜잭션 안에서만 동작되기 때문에 순간 사용하는 캐시이므로 성능 이점을 크게 가져가지 못합니다.

 

동일성(identity) 보장

AppFormEntry findAppFormEntry = em.find(AppFormEntry.class, 100L);
AppFormEntry findAppFormEntry2 = em.find(AppFormEntry.class, 100L);
System.out.println(findAppFormEntry == findAppFormEntry2);

위에서 말했던 1차 캐시로 Repeatable Read 등급의 트랜잭션 격리 수준을 DB가 아닌 애플리케이션 차원에서 제공해줍니다.

 

트랜잭션을 지원하는 쓰기 지연

EntityTransaction tx =  em.getTransaction();

em.persist(findAppFormEntry);
em.persist(findAppFormEntry2);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않습니다.

//커밋하는 순간 데이터베이스에 INSERT SQL을 보냅니다.
tx.commit();

 

Dirty Checking

JPA에서는 update가 따로 필요하지않습니다. 

Member member = em.find(Member.class, 100L);
member.setName("hello1234");

em.persist(member);
tx.commit();

member의 Id 100L으로 조회해서 객체를 찾았습니다. 찾은 뒤에 객체의 이름을 변경하고 commit을 하면 어떻게 될까요? 

위에 코드에서 어딜봐도 update이 없습니다. 어떻게 update 된 걸까요? 그건 바로 Dirty Checking 즉 변경 감지인데요 영속성 컨텍스트 안에서 객체가 변경을 감지해서 만약 변경되었다면 업데이트를 해주는 겁니다. 

메커니즘 간략하게 설명해보겠습니다.

내부적으로 flush()를 호출 -> 1차 캐시안에 있는 스냅샷을 생성 -> 엔티티가 변경이 되었다면  Entity와 스냅샷과 비교 -> 쓰기 지연 SQL 저장소에 Update SQL 생성 -> flush가 데이터베이스에 반영 -> commit

 

여기서 궁금증이 생겼습니다. 만약 스냅샷 비교 후 update SQL을 생성했다면 기존 1차 캐시에 있는 Entity와 스냅샷은 다른 상태로 있을지 아니면 같아질지 궁금했습니다. 찾아보니 UPDATE SQL 생성 후 flush를 통해 변경감지가 진행되면, 1차 캐시에 있는 Entity와 스냅샷은 같아집니다. (commit 직후에는 영속 컨텍스트가 없어지져서 상관없지만 그래도 궁금했기에...)

 

스터디 끝!

반응형

댓글