본문 바로가기

Back End/Spring Boot

[Spring JPA] JPA의 영속성(Persistence)에 관하여

JPA가 갖는 영속성(Persistence)에 관하여


개요

  JPA가 갖는 영속성(Persistence)이라는 특징에 대해 집중적으로 알아본다.

 

목차

 

소개

JPA의 영속성 컨텍스트(Persistence Context)란?
영속성(Persistency)는 지속 혹은 영구적이라는 의미이다. 즉, 영속성 컨텍스트란 객체로 관리되는 엔티티를 영속화시켜 관리하는 논리적인 공간이라고 할 수 있다.

 

그렇다면 이러한 영구적인 공간은 왜 등장하게 되었을까?

 

JPA는 객체지향언어인 JAVA와 DB의 다른 체계를 일치시키기 위해 등장하였다. 하지만 서로 다른 두 패러다임을 일치시키기 위해 DB의 데이터를 계속하여 조회해 오고, 객체의 값이 변함에 따라 DB를 호출해 객체와의 데이터를 일치시키는 것은 에러가 발생했을때 DB의 데이터들을 손상시키는 위험을 가져올 수 있다. 이를 해결하기 위해 등장한 개념이 영속성 컨텍스트이다.

 

어떻게 DB의 데이터들이 손상되는 위험을 줄여줄까?

 

위의 질문에 대한 해답을 찾기위해, 먼저JAVA코드가 어떻게 DB의 내용들을 조회하고 수정하는지 알아보자.

  1. JAVA의 객체는 먼저 JPA에 조회 혹은 변경하고자 하는 데이터들의 정보를 전달한다.
  2. JPA는 전달받은 객체의 정보를 Query로 변환하여 DB에 접근한다.
  3. 전달받은 DB의 정보를 다시 JAVA객체에 전달하여준다.

이렇게 전달받은 객체의 값을 수정하면 JAVA가 갖고 있는 객체, 읽어온 객체에서 값을 변경한 객체, DB에 저장되어 있는 데이터값은 항상 같다고 할 수 있을까? ORM의 등장 배경 이전에는 이를 실제로 구현하는것이 상당히 복잡하였다.

 

이때, ORM의 등장 그리고 JPA가 등장하면서 "영속성 컨텍스트"의 개념으로 위의 문제에 대한 해결책을 제시해주었다.

영속성 컨텍스트는 EntityManager를 활용하여, 객체가 여러번 수정되고 조회되어도 참조값을 잃지 않도록 관리해주며 데이터들이 손상되고 변경되어 DB에 저장되는것을 해결해주게 되었다. 그렇다면 영속성 컨텍스트의 장점은 이것이 전부일까? 아니다.

 

영속성의 개념을 더 확장한 1차 캐싱

 

JPA는 참조값을 잃지 않도록 데이터를 어떻게 관리할까? 여기서 사용되는 개념이 1차 캐싱이다. JPA는 데이터가 처음 호출 될 때 DB에 접근하여 값을 가져온다. 그리고 가져온 데이터(객체)를 SnapShot 형태로 저장해서 관리하게 된다.

이를 통해 EntityManager로 PK를 통한 데이터 조회 요청시, 영속성 컨텍스트에서 1차 캐시 저장소를 활용해 데이터를 가져올 수 있다.

이때 1차 캐시 저장소에 데이터가 저장되는 방법에는 크게 두가지가 있다.

  1. 만약 PK를 통해 데이터를 조회했을 때 1차 캐시 저장소에 저장되어 있지 않다면 DB에서 데이터를 조회하여 값을 가져온 후 1차 캐시 저장소에 저장한다.
  2. Hibernate는 primary key로 1차 캐시 저장소에서 값을 유지관리 하므로, PK가 아닌 다른 컬럼값으로 조회를 할 경우 값을 찾을 수 없어 JPQL로 DB에서 데이터를 조회해온 후, 조회해온 데이터를 1차 캐시 저장소에 저장한다.

이를 통해, JPA는 영속성 컨텍스트 안의 1차 캐시 저장소를 캐시처럼 활용하여 데이터를 빠르게 가져올 수 있게 된다.

 

쓰기지연(Lazy Loading)을 통한 DB값들의 컨트롤

 

서버의 로직상에서 실시간으로 변경되는 데이터들을 DB에 실시간으로 반영하는것은 과연 효율적일까? 값이 변경될때마다, DB에 접근하여 값을 수정해주는것은 많은 시간을 소요시킬뿐만 아니라, 에러가 발생하여 데이터를 RollBack 시켜야 할때도 많은 시간을 소요시키게 된다.

이때, 쓰기지연은 Update나 Insert하는 Query문들을 위의 쿼리 저장소에 저장시켜두어 Flush 혹은 Commit이 끝나는 시점에 일괄적으로 DB에 값을 변경시켜준다. 이를 통해, DB의 접근 횟수를 줄여주어 시간을 단축시켜줄 수 있다. 하지만, DB에 접근하지 않고도 DB에 반영될 값들을 컨트롤할 수 있다는 것이 쓰기지연에서 가장 중요한 개념이라고 할 수 있다.

 

그렇다면 쓰기지연을 발생시키는 Update와 같은 Query문들은 발생할때마다 작성해주어야 하는것일까?

우리는 이러한 작업을 Dirty-Checking을 통해 해결할 수 있다.

 

변경을 감지하여, Update Query를 작성해주는 Dirty-Checking

 

JPA는 Set()과 같은 메소드와 같이 객체를 변경하는 기능이 수행될때마다, 이를 추적하여 객체를 관리해준다. 이를 통해 JPA는 Update Query문을 자동으로 생성해주고 영속성 컨텍스트에서 유지해두었다가, Flush 혹은 Commit이 일어나는 시점에 DB에 Query를 날려주는 것이다. 물론 이러한 Update Query문은 기본적으로 모든 Column을 업데이트 하게 됨으로 불필요한 동작이 일어나는 단점은 있다. 이는 JPA의 DynamicUpdate @Annotation으로 해결할 수 있으므로 큰 문제가 되지는 않는다.

 

이처럼 영속성 컨텍스트를 통해 JPA는 1차 캐싱, 쓰기지연, Dirty-Checking, 객체의 일관성 유지등 많은 기능들일 이끌어준다. 이를 실제 EntityManager를 통해 어떻게 유지관리 하는지는 다음 블로그글에서 다루어볼 예정이다. ORM이 등장하고 JPA가 등장하면서 JAVA는 객체지향적인 개발을 위해 많은 기능들을 제공하고 있다. 이러한 기능들을 바탕으로, 객체지향적인 개발을 해 나아갈 수 있도록 끊임없는 공부가 필요할 것 같다.