본문 바로가기
프로그래밍/Back-end

JPA 영속성 컨텍스트의 장점에 대하여!

by @GodWin 2024. 10. 15.

-

 

-
안녕하세요? 오늘은, JPA에서 가장 중요한 개념인, 영속성에서 더 나아가서, 영속성 컨텍스트의 이점에 대해서 알아보도록 하겠습니다.


※ JPA의 영속성과 영속성 컨텍스트에 대해서는, 아래 포스팅을 참조하시면 됩니다.

https://logger-debug.tistory.com/entry/JPA-%EC%98%81%EC%86%8D%EC%84%B1%EA%B3%BC-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC

 

JPA 영속성과 영속성 컨텍스트에 대하여!

- -안녕하세요? 오늘은 JPA에서 가장 중요한 개념인, 영속성에 관련해서 알아보도록 하겠습니다.JPA를 공부할 때 가장 중요한 점은,1. 객체와 관계형 데이터베이스를 매핑하는 것(Object Relational Mapp

logger-debug.tistory.com


자 그럼, 영속성 컨텍스트의 이점에 대해서 알아보도록 하겠습니다.


-
영속성 컨텍스트의 이점은 뭘까요?

그건 바로, 1차 캐시, 동일성 보장 (Identity), 트랜잭션을 지원하는 쓰기 지연, 변경 감지, 지연 로딩 입니다.
각각의 이점에 대해서, 자세히 알아보도록 하겠습니다.

 

1차캐시

: 트랜잭션의 범위 안에서만 사용하는, 짧은 캐시 레이어
※ 글로벌하게 사용되는 캐시는 2차캐시라고 합니다.

영속성 컨텍스트(EntityManager)에는 내부에 1차캐시가 존재합니다.
엔티티를 영속성 컨텍스트에 저장하는 순간 1차캐시에 등록이 됩니다.

find가 일어나는 시점에서는, EntityManager 내부의 1차캐시에서 먼저 데이터를 찾게 됩니다.
1차캐시에 Entity가 존재한다면 해당 데이터를 반환하게 되고, 만일 해당 데이터가 1차캐시에 존재하지 않는다면 DB를 들러서, 해당 데이터를 1차캐시에 등록하게 되고, 해당 데이터를 반환하게 됩니다.

※ 1차캐시는, 글로벌하지 않고, 스레드 하나가 시작되고 끝낼때까지만 사용하게 되고, 공유하지 않는 캐시인 점을 염두해두셔야합니다.



EntityManagerFactory emf = Persistence.createEntityManagerFactory("유닛명");
EntityManager em = emf.createEntityManager();

Entity entity = new Entity();
entity.setId("entityA");
entity.setUsername("엔티티A");

// 1차 캐시에 저장됨
em.persist(entity);

// 1차 캐시에서 조회
Entity findEntity = em.find(Entity.class, "entityA");

tx.commit();

 

 

영속 엔티티의 동일성(Identity) 보장

: 1차캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을, 데이터베이스가 아닌 애플리케이션 차원에서 제공
: 1차 캐시 덕분에 데이터를 두번 조회해도 다른 객체가 아니다. (같은 레퍼런스)

 

EntityManagerFactory emf = Persistence.createEntityManagerFactory("유닛명");
EntityManager em = emf.createEntityManager();

Entity a = em.find(Entity.class, "entityA");
Entity b = em.find(Entity.class, "entityA");

System.out.println(a == b); // 동일성 비교 true

 

 

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

(Transactional write-behind) - 엔티티 등록

 

: 트랜잭션 내부에서 persist가 일어날 때, 엔티티들을 1차캐시에 저장하고, 논리적으로 쓰기 지연 SQL 저장소 라는 곳에 Insert 쿼리들을 쌓아두고, 트랜잭션 commit 시점에 DB에 해당 쿼리들을 전송한다.

※ 쿼리를 보내는 방식은, 동시/개별 옵션에 따라 다르다.
<property name="hibernate.jdbc.batch_size" value=10/>


쌓여있는 쿼리들을 DB에 보내는 동작을 flush라고 한다.
flush는 1차캐시를 지우지 않고, 쿼리를 DB에 날려서 DB와 싱크를 맞추는 역활을 한다.
트랜잭션을 커밋하게 되면, flush와 commit 두가지가 수행이 된다.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("유닛명");
EntityManager em = emf.createEntityManager();

EntityTransaction transaction = em.getTransaction();
// 엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // 트랜잭션 시작
​
em.persist(entityA);
em.persist(entityB);
// 이때까지 INSERT SQL을 데이터베이스에 보내지 않는다.
​
// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // 트랜잭션 커밋

 

 

변경 감지(Dirty Checking) - 엔티티 수정


: 데이터를 변경을 했을 시에, 어떻게 JPA에서는 자동으로 업데이트 쿼리가 실행되는가 ?
-> 데이터만 set으로 변경하고 트랜잭션을 커밋하면 자동으로 업데이트 쿼리가 나가게 되는데, 1차캐시에 데이터를 저장할때, 스냅샷 필드를 저장을 하게 되는데, commit (내부적으로 flush 호출)이 일어날 때, 엔티티와 스냅샷을 비교해서, 변경사항이 있으면 Update 쿼리를 DB에 전송

EntityManagerFactory emf = Persistence.createEntityManagerFactory("유닛명");
EntityManager em = emf.createEntityManager();

EntityTransaction transaction = em.getTransaction();
transaction.begin(); // 트랜잭션 시작

// 영속 엔티티 조회
Entity entityA = em.find(Entity.class, "entityA");

// 영속 엔티티 수정
entityA.setUsername("nj");
entityA.setAge(27);

//em.update(entityA) 또는 em.persist(entityA)로 다시 저장해야 하지 않을까?

transaction.commit(); // 트랜잭션 커밋

 

 

값을 변경하면 변경된 리스트가 유지되는 것과 같은 컨셉

 

엔티티 삭제


: 변경 감지로 엔티티 수정과 같은 매커니즘으로 실행

EntityManagerFactory emf = Persistence.createEntityManagerFactory("유닛명");
EntityManager em = emf.createEntityManager();

Entity entityA = em.find(Entity.class, "entityA");

em.remove(entityA); // 엔티티 삭제

 



오늘은, JPA에서 가장 중요한 개념인, 영속성에서 더 나아가서, 영속성 컨텍스트의 이점에 대해서 알아보았습니다.
다음 포스팅에서는, flush에 대해서 자세히 알아보도록 하겠습니다.
그럼 오늘도 즐거운 하루 되시길 바라겠습니다.