백앤드(Back-End)/Jpa

[JPA] - 영속성 컨텍스트(2)

RyanSin 2024. 11. 29. 01:57
반응형

개요

안녕하세요. 이번 시간에는 JPA에서 조회 및 쓰기 그리고 수정 동작에 대해 알아보겠습니다.

 

1차 캐시, 쓰기 저장소, 변경 감지에 대한 개념에 대해서도 같이 설명하겠습니다.

 

혹시 지난 시간에 내용을 학습하고 오시지 못하신 분들은 아래 링크를 통해 학습하고 오시는 걸 추천드리겠습니다.

[JPA] - 영속성 컨텍스트(1)

 

[JPA] - 영속성 컨텍스트(1)

개요안녕하세요. 이번 시간에는 JPA 영속성 컨텍스트에 대해 알아보겠습니다. 지난 시간 내용을 학습하고 오시지 못하신 분들은 학습하고 오시는 걸 추천드리겠습니다. [JPA] - 기본 동작 방식과

any-ting.tistory.com

 

 

1차 캐시

JPA 영속성 컨텍스트 안에는 1차 캐시라는 개념이 존재합니다.

 

1차 캐시라는 개념은 실제 우리가 생각하는 캐시 메모리 같은 하나의 공간이라고 생각하시면 됩니다.

 

Select 쿼리를 실행하면 다음과 같은 순서로 실행됩니다.

  1. 1차 캐시에 조회하는 엔티티가 존재할 때
    1. 1차 캐시에 엔티티를 조회합니다.
    2. 1차 캐시에 엔티티가 존재하면 해당 엔티티를 반환합니다.
  2. 1차 캐시에 조회하는 엔티티가 존재하지 않을 때
    1. 1차 캐시에 엔티티를 조회합니다.
    2. 1차 캐시에 엔티티가 존재하지 않기 때문에 실제 DB에서 데이터를 조회합니다.
    3. 조회한 데이터를 1차 캐시에 등록합니다.
    4. 등록된 후 해당 엔티티를 반환합니다.

1차 캐시

 

위 그림처럼 1차 캐시가 엄청난 성능을 올려주지는 않습니다. 대신 다음과 같은 메커니즘을 이해하는 게 좋습니다.

fun findMemberByName(em: EntityManager): MemberEntity {
    val memberEntity = em.find(MemberEntity::class.java, 1L)
    println("Member Id = ${memberEntity.id}")
    println("Member Name = ${memberEntity.name}")
    return memberEntity
}

 

 

쓰기 저장소

지난 시간에 엔티티를 등록하면 Insert 쿼리가 실행되지 않는다고 설명했습니다.(GenerationType.IDENTITY 설정하지 않았을 때)

 

JPA는 내부적으로 쓰기 지연 저장소라는 특정한 공간을 활용해서 Insert 쿼리를 즉시 실행하지 않고 쿼리를 모아뒀다가 트랜잭션을 완료하는 시점에 한 번에 Insert 쿼리 구문을 실행하는 방식을 사용합니다.

 

persist() 메서드를 실행하면 다음과 같이 동작합니다.

쓰기 저장소 등록

 

그런 다음 실제 commit() 또는 flush() 메서드를 호출하면 그때 DB에 쿼리 구문이 실행됩니다.

Insert 쿼리 구문 실행

 

아래 코드를 살펴보면 하나의 트랜잭션 안에 위와 같이 동작을 진행합니다.

fun createMember(em: EntityManager) {
    val tx = em.transaction
    tx.begin()
    try {
        val member = MemberEntity(name = "HelloA")
        em.persist(member)
        tx.commit()
    } catch (e: Exception) {
        tx.rollback()
    }
}

 

 

변경감지(Dirty Checking)

JPA에서는 실제 엔티티를 수정하고 그 수정한 내용을 DB에 반영할 때는 update()와 같은 메서드를 사용하지 않습니다.

 

영속성 컨텍스트 안에 내부적으로 엔티티마다 버전을 관리하고 엔티티에 변경이 발생했을 때 그때 Update 쿼리가 실행됩니다.

 

변경 감지

 

중요한 포인트는 1차 캐시에 해당 엔티티가 영속성 상태일 때 변경감지가 실행되고 엔티티가 수정됩니다.

 

fun updateMember(member: MemberEntity, em: EntityManager) {
    val tx = em.transaction
    tx.begin()
    try {
        member.name = "HelloB"
        tx.commit()
    } catch (e: Exception) {
        tx.rollback()
    }
}

 

 

이번 시간에는 조회, 쓰기, 수정 동작에 대해 알아봤습니다. 그 속에 JPA에서 사용되는 1차 캐시, 쓰기 지연 저장소 그리고 변경감지에 대해 알아봤습니다.

 

실습을 통해 꼭 확인해보시는 걸 추천드리겠습니다.