티스토리 뷰

 

JPA를 공부하면서 개념과 동작 원리를 알기 전에 실전 예제부터 따라했다

그 때 JPA가 Mybatis와 달리 정말 마법처럼 모든 걸 해결해 줘서 대체 어떻게 동작하는 걸까 궁금했는데

예제를 따라한 후 개념을 습득하니 명쾌하게 이해가 되었다

처음에는 알아듣기 어려웠지만, 이해하고 나니 쉬웠는데,

나처럼 JPA를 입문하며 어려움을 겪을 사람들을 위해 그림과 코드로 정리해보았다

 

 

들어가기 전에

본 포스팅은 김영한 님의 자바 ORM 표준 JPA 프로그래밍 - 기본편을 수강하며 공부 정리로 작성한 포스팅입니다.

해당 강의가 명강의이니 JPA 공부를 시작하시는 분들에게 강력 추천합니다.

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

 

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

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

www.inflearn.com

 

영속성 컨텍스트란?

영속성 컨텐스트는 엔티티가 영구 저장되는 환경을 뜻하는 논리적인 개념이다

엔티티 매니저를 통해 접근할 수 있으며 이하 그림에서는 강아지(엔티티 매니저)가 작업하는 공간으로 설정했다

 

 

우선 DB에 PK로 들어갈 Id 값과 문자열 이름을 갖는 Member 라는 엔티티를 만들었다

@Entity
public class Member {
    @Id
    private Long id;
    private String name;
    
    //getter, setter 생략
}

 

 

비영속 상태란

고양이(코드를 짜는 우리)의 목표는 id와 name이 각각 1, "A"인 데이터를 DB에 넣고 관리하는 것이다

Member memberA = new Member();
memberA.setId(1L);
memberA.setName("A");

여기까지 상태에서 memberA는 영속성 컨텍스트 환경에 들어가지 않은 완전히 무관한 객체이다

이를 비영속 상태 라고 한다

 

 

 

비영속에서 영속으로 

이제 엔티티매니저를 생성하고 엔티티매니저를 통해 persist로 객체를 영속 상태로 만든다

아래 코드에서는 엔티티매니저 팩토리, 매니저, 트랜잭션을 생성하는 과정은 생략하였다

해당 과정은 필요하다면 이전 포스팅에서 볼 수 있다 

em.persist(memberA);

이제 memberA는 영속성 컨텍스트에 의해 관리되는 영속 상태가 되었다

유의할 점은 persist를 한다고 해서 바로 DB에 저장되는 것이 아니란 점이다

현재는 1차 캐시에 저장되어 있을 뿐이다

그리고 재미있게도 1차 캐시에 객체가 올라가면서 스냅샷 이라는 것이 자동으로 생성된다

 

이 스냅샷에는 객체의 원래 상태 정보가 저장되어 있다

쓰임새는 후에 설명하겠다

 

여기서 memberB 라는 객체를 추가로 생성하고 등록하면 이런 그림이 된다

Member memberB = new Member();
memberB.setId(2L);
memberB.setName("B");

em.persist(memberB);

여전히 DB에는 실질적으로 저장된 데이터는 0건이다

 

 

 

em.find()의 과정

이제까지는 엔티티매니저를 통해 id값을 조회하는 것이 DB의 값을 찾아오는 것이라 생각했다

예제 코드를 짜면서 어렴풋이 그게 아니란 것은 알게 되었지만, 개념을 공부하고 알게 된 진짜 과정은 이러했다

 

 

em.find를 통해 1L id값을 갖는 Member 엔티티를 조회하면 엔티티매니저는 1차 캐시를 제일 먼저 조회한다

그래서 아직 DB의 MEMBER 테이블은 비어있지만, 1차 캐시에 있는 memberA가 조회되어 반환된다

이는 영속성 컨텍스트에 의해 관리되기에 가능한 것이다

물론 DB에 해당 정보가 들어간 후, 1차 캐시가 비워진 상황이라면 1차 캐시 조회 후 DB 조회하여 반환하게 된다

 

 

em.flush()와 쓰기 지연 

다시 em.persist(memberA)로 돌아가서, 새로운 엔티티가 영속성 컨텍스트에 올라갈 때 쓰기 지연이 일어난다

쓰기 지연이란, SQL 구문이 바로 실행되지 않고 저장소에 쌓이는 것을 뜻한다 

이제 고양이가 flush를 호출하면, 저장소에 쌓였던 SQL문이 실제로 실행되게 된다

 

em.flush();

실제 DB에 데이터가 삽입되는 것은 이 시점에서다

flush는 직접 호출할 수도 있고, 기본적으로 트랜젝션이 커밋되는 시점에 자동으로 호출된다

SpringBoot Framework와 함께 사용한다면 따로 신경쓸 일은 많지 않을 것이나,

DB 반영이 반드시 선행되어야 할 때는 위와 같이 flush 호출로 쌓인 SQL 구문을 실행시키면 된다 

 

 

변경 감지 (Dirty Checking)

위에서 얘기한 스냅샷은 변경 감지에 사용된다 

flush가 실행되기 전으로 돌아가서, 고양이가 memberA를 찾아 정보를 수정한다고 해보자

 

Member findMember = em.find(Member.class, 1L);
findMember.setName("CAT");
em.flush();

고양이가 id 값으로 memberA를 찾았고, 엔티티매니저는 1차 캐시에서 memberA를 반환하였다

그리고 findMember에 저장된 memberA의 이름을 CAT으로 바꾸고, flush를 호출했다

 

그러면 실제로 SQL 구문이 실행되기 전에 변경 감지가 일어난다

변경 감지란, 영속성 컨텍스트 안의 모든 엔티티의 현재 상태와 스냅샷을 하나하나 비교하여,

변경된 사항이 있다면 자동으로 SQL 구문을 추가하는 것을 말한다

그림에서는 memberA의 name이 스냅샷에서는 A, 현재 상태에서는 CAT으로 변경되었다

 

감지된 변경점을 토대로 UPDATE 문이 작성되고, 저장소에 쌓이는데

만약 memberB의 이름이 변경되었다면 memberB에 대한 UPDATE문도 추가되었을 것이다

 

SQL 구문이 실행되고 나면 테이블에는 데이터가 이렇게 들어가게 된다

 

 

 

준영속과 삭제

em.detach(memberA);

detach를 통해 객체를 준영속 상태로 만들 수 있다

준영속 상태란 영속성 컨텍스트에서 분리된 상태를 뜻한다

 

이 상태에서 memberA의 이름을 GRAY로, memberB의 이름을 DOG으로 바꾸고 flush를 한다고 가정하자

그러면 영속성 컨텐스트 안에서만 변경 감지가 되기에, SQL 저장소에는 memberB의 UPDATE 구문만 들어가게 된다

실제로 DB에도 memberA의 변경 내용은 반영되지 않는다

 

 

 

em.remove(memberA);

remove는 해당 객체를 삭제하고, DB에서도 삭제할 때 사용한다

객체를 remove할 시 DELETE 구문이 나가게 된다 

 

 

 

 

이상으로 JPA의 영속성 컨텍스트 개념에 대해 알아보았다

이 글이 JPA에 입문하면서 나처럼 혼란을 겪었던 다른 사람들에게 도움이 되면 좋겠다

부정확한 표현이나 틀린 설명이 있다면 알려주시길 바란다 

 

 

개와 고양이 일러스트의 출처

www.irasutoya.com/

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글