[JPA] 영속성 컨텍스트의 특징 및 기본 구조

윤들윤들·2020년 12월 20일
1
post-thumbnail

:) 영속성 컨텍스트의 특징


  • 영속성 컨텍스트와 식별자 값
    @Id 어노테이션으로 테이블의 기본키와 매핑된 값으로 구분한다. 따라서 영속 상태의 엔티티에서는 식별자 값이 필수이다. 만약 영속 상태에서 식별자 값이 없으면 예외가 발생한다.

  • 영속성 컨텍스트와 데이터 베이스 저장
    JPA 에서는 대게 트랜잭션이 끝나는(commit) 순간 데이터베이스에 영속성 컨텍스트에 있는 내용을 반영하는데 다음과 같은 과정을 flush라고 한다

  • 영속성 컨텍스트가 엔티티를 관리하게 되었을때 얻을 수 있는 장점.
    1차캐시, 동일설 보장, 트랜잭션을 지원하는 쓰기 지연, 변경 감지, 지연로딩


:) 엔티티 등록

영속성 컨텍스트에는 내부 캐시를 가지고 있을 수 있는 공간이 있는데 이것을 1차 캐시라고 부른다. 영속상태인 모든 엔티티는 모두 이곳에 저장된다.

그림으로 확인하겠지만 영속성 컨텍스트 내부에는 Map이 있는데 @Id 어노테이션으로 식별한 식별자 값이 주키 입니다.

Member member = new Member("YundleYundle", 28);
entityManager.persist(member);

tx.commit();

위와 같은 코드를 동작 시켜 엔티티를 영속성 컨텍스트에 등록하였을경우 1차 캐시는 다음과 같이 엔티티를 저장하고 있을것입니다.

사진에서 보이는것처럼 Map(1차캐시) 안에서의 키는 @Id어노테이션으로 지정했던 그림에서 보이는 member1이 key가 되고 해당 Entity객체가 Value가 됩니다.

또한 @Id가 데이터베이스의 PK와 매핑되기 때문에 저장하고, 조회하는 모든 기준은 @Id, DB의 PK가 된다.

또한 등록같은 경우 트랜잭션을 커밋하기전까지 데이터베이스에 Entity를 저장하지 않고 내부 쿼리 저장소에 SQL을 모아두었다가 트랜잭션을 커밋 할때 데이터베이스에 전송한다.

이러한 특징을 트랜잭션을 지원하는 쓰기 지연이라고 한다


:) 엔티티 조회

엔티티를 저장했다면 조회할때는 과연 어떻게 동작하는걸까
제 생각에 다음이 제일 중요한 설명일듯 싶다.

1차 캐시에서 먼저 엔티티를 찾았지만 1차캐시에 존재하지 않을 경우 데이터베이스에서 조회한다. 그다음 1차캐시에 등록한다음 그 갚을 반환(return)한다

find()같은 경우는 EntityManager안에 구현된 interface를 보면 다음과 같이 구현되어 있습니다.

// 찾고자 하는 Entity 의 타입과 , 식별자 값을 넣어주면 됩니다.
<T> T find(Class<T> var1, Object var2)

그러면 위에 등록한 member를 찾기위해서는 어떤 코드를 적어야 하고 어떻게 동작하는지 확인해보겠습니다.

Member findMember =  entityManager.find(Member.class, "member1");

위의 사진처럼 식별자 member1을 찾으려고하는데 1차 캐시에 존재 할경우 데이터베이스에서 조회 하지 않고 메모리에 있는 1차 캐시에서 엔티티를 조회한 후 반환한다.

그렇다면 1차캐시에도 없는 데이터를 조회할 경우에는 어떻게 할까?

위와 같은 그림으로 정리 할 수 있다.

조회시 해당되는 식별자값의 Entity가 1차캐시에 존재하지 않는다면 데이터베이스로 부터 식별자 값에 매핑되는 row데이터를 읽어와 1차캐시에 등록한 후 1차캐시에 저장된 해당 Entity를 반환한다.


:) 영속 엔티티의 동일성은 보장이 될까

Member member1 = em.find(Member.class, "member1");
Member member2 = em.find(Member.class, "member1");

System.out.println(member1 == member2);

위의 코드는 과연 true 일까요 false일까요.

JPA에서는 조회시 1차캐시에 있는 Entity를 반환해주기때문에 두개의 객체는 같은 Entity 객체를 전달 받습니다.

그렇기 때문에 해당 true를 반환한다. 영속성 컨텍스트는 성능상 이점과 Eitity의 동일성을 보장해 준다


:) 엔티티의 수정

등록도 하고 조회도 하였으니 이제는 수정에대해 알아보겠습니다.

데이터의 수정같은 경우는 EntityManager에서 update()같은 메서드를 지원하지 않는다. 이는 Entity의 변경 사항을 데이터베이스에 자동으로 반영하는 변경 감지 기능을 지원한다

업데이트되는 방식은 다음과 같습니다.

  1. 트랜잭션 커밋시에 EntityManager 내부에 있는 flush가 먼저 호출된다.
  2. 그다음 EntitySnapshop과 비교하여 변경된 Entity를 찾는다.
  3. 변경된 Entity가 존재하면 Update Query를 생성한다음 지연쓰기를 통해 SQL 저장소에 보관
  4. 쓰기 지연 저장소의 SQL을 데이터베이스에 전송한다.
  5. 데이터베이스 트랜잭션을 커밋한다.

단 엔티티의 변경감지는 영속성 컨텍스트가 관리하는 영속 상태의 Entity에게만 적용된다. 비영속 또는 준영속 상태처럼 엔티티 컨텍스트의 관리를 받지 못하는 Entity는 값을 update해도 데이터베이스에 반영되지 않는다.

JPA의 수정은 변경된 부분만 업데이트 하는것이 아니라 해당되는 Entity의 모든 필드를 업데이트 한다

모든 필드가 업데이트됨으로 데이터베이스에 보내는 SQL의 전송량이 증가한다는 단점이 있지만. 모든필드를 사용하면 수정쿼리가 같고, 로딩 시점에 수정쿼리를 미리 생성해두고 재사용 할 수 도있다는 장점이있다.

하지만 필드가 너무 많아서 너무 불필요 하기 때문에 변경된 부분만 SQL로 보내고 싶다면 하이버네이트의 확장된 @org.hibernate.annotations.DynamicUpdate 어노테이션을 사용하면된다.


:) 엔티티 삭제

Entity를 삭제하기 위해서는 먼저 해당 Entity를 조회해야 합니다.

remove()메서드는 다음과 같이 되어있습니다.

void remove(Object entity);

remove에는 파라미터로 조회된 Entity를 넘겨줘야한다는 말입니다.

remove() 또한 remove()를 한다고 바로 실행되는것이 아니라 쓰기 지연 SQL 저장소에 등록된후 트랜잭션이 끝나는 시점에 실제 데이터베이스에 삭제 쿼리를 전달한다.
( 단 remove(entity)를 할경우 쓰기 지연 SQL저장소에 저장되지만 1차 캐시에서는 제거된다.)


profile
Front&BaackEnd를 재미있게 공부하고싶은 개발자 YundleYundle

0개의 댓글