DevBoi

[JPA] JPA 영속 컨텍스트 본문

Develop/[JPA]

[JPA] JPA 영속 컨텍스트

HiSmith 2021. 7. 26. 18:23
반응형

JPA는 느끼다싶이 객체를 넘겨서 대부분의 데이터작업을 진행한다.

그런데, 쿼리가 복잡하거나, 통계성쿼리가 필요하다면?

이건 객체로 푸는 것보다 쿼리로 푸는것이 맞다.

 

이때 JPA에서는 JPQL이라는 녀석으로 극복할수있게해준다.

근데, JPQL은 조금 다르다. 예를들어, MEMBER 테이블에서, 해당 대상을 전부 가져오는 걸 생각해보자

테이블 대상이라면, select * from member 뭐 이런식으로나, select id from member

이런식으로, 짠다.

 

jpql도 역시 객체 지향쿼리여도 sql짜듯이 짜면된다.

강점은 언뜻 보기에 두가지 정도가있다.

 

1. 쿼리를 객체에 맞춰서 짜고, 방언에 따라, mysql,oracle등으로 자동 변환.

2. 페이징 관련 속성 set식으로, 자동 생성

 

 

쉽게 JPQL은 SQL을 추상화 하여 만든 객체 지향형 sql이라고 생각하면 된다,

 

 

영속성 컨텍스트

-> jpa를 이해하는데 가장 중요하다.

 

엔티티를 영구 저장하는 환경이라는 뜻이다. (EntityManager.persist)

엔티티는 생명주기가 있다.

 

- 비영속 상태 ( new 개념 ) 

영속성 컨텍스트와 관계가 없다.

쉽게 말하면, 단순 객체를 new한 상태이다.

Member member = new Member();

member.setId("gd");

 

뭐이런식으로, Member 단순 생성을 한 상태이다. 이건 비영속 상태이다.

 

- 영속 상태

위에서 생성한 Member를 EntityManger의 transaction을 가져온 상태에서

persist(객체를 하면된다.)를 하거나, find로 가져와 객체 담는 것을 의미한다.

 

 

쉽게 얘기하면, 위에 간단하게 짠 코드로 이해하면된다.

위에 Entitymanger 위까지가 비영속 상태, Manager에 persist로 넣은순간, 영속 상태가 된다.

 

해당 웹을 재기동하여, persist까지하여도, 영속상태에서 끝이 나기때문에, db에는 저장이 일어나지않는다.

다시 말하면 영속상태, 비영속 상태는 db와 연관이없고, jpa에서 관리하는 영속성 컨텍스트에 저장이 되어있고

안되어있고에 따라 다른 것이고  디비에 저장과 무관한 것이다 ( db 쿼리도 나가지 않는다).

 

자 그러면 언제 도대체 나갈까?

이해를 좀더 편하고, 직관적으로 보기 위해, log가 아닌 sysout으로 프린트를 했다.

EM 을 통해 트랜잭션을 get하고, 프록시 처럼 persist 이후, commit 이전이후에 출력을 해서 언제쯤인지 봤다.

 

 

 

트랜잭션의 커밋시점에 db sql이 날라가는것을 볼수있다.

이로써, 영속,비영속은 db와 관련이 없다는 것을 알수 있다.

 

-준영속 상태

persist와 반대 개념이다. detach 를 통해, 분리를 하는 것이며, 이떄는 트랜잭션에서 빠지게 된다.

 

 

-삭제 상태

이건 뭐 그냥 delete 날리는 것과 동일하다고 생각하면된다.

그냥 삭제하는 것이다. db에서도, 컨텍스트에서도 

 

영속성 컨텍스트를 그러면 왜쓸까?

 

- 1차 캐시 (하나의 트랜잭션 내부에서 이루어진다.)

@id와 Entity로 이루어진 캐시의 값이 있다.

조회를 할때 이 1차 캐시는 장점이 있다. 

jpa는 조회를 할때, 1차캐시를 우선적으로 탐색을 하고, db를 조회한다.

db를 조회한 내용을 1차 캐시에 올리고 반환한다. (1차 캐시에 없었다면)

 

insert쿼리가 나가기전에, select 쿼리가 돌지 않았는데,

위에서 persist했고, 1차 캐시에 저장이 되었기 때문에, 해당 pk를 가지고 find를 하게되면

해당 값을 가져와서, 보여주는 것을 확인

 

- 동일성 보장

동일성 보장이란 자바에서 Memeber 에 대한 객체를 두개를 사용한다고 생각해보자

2개의 객체는 , 각각 db에서 동일하게 값을 return 해서 넣은 객체라고 가정해보자

자바에서 객체에 대한  == 으로한다면? 주소가 다르기 때문에 false가 난다.

하지만 find가 동일한, 내용으로  객체에 넣는 jpa라면?

true가 난다.

예를 살펴보자

두번 조회했지만, 쿼리는 한번 만 나가고 객체 비교도 true로 나온다.

 

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

트랜잭션 단위로 commit하는 순간, sql들이 한꺼번에 몰려나간다고 생각하면된다.

즉, 즉시 sql이 나가는게 아니라, 트랜잭션이 끝난 이후에 나간다.

flush개념으로 이해하면된다.

 

 

- 변경 감지 (dirty checking)

 

전에 포스팅 했던 것처럼, update나, 변경이 발생할때 일일히 persist할필요없이

단순히 set하면 이렇게 update쿼리가 나간다.

 

어떻게 동작하는지가 중요하다.

1차 캐시에서는, 스냅샷 개념으로, 최초 entity 읽어온 시점에 값을 가지고있다.

jpa에서 commit을하여, flush가 되는 시점에 이 entity 값과 스냅샷의 값을 비교해서,

다른 것이있다면, commit 시점에 update 쿼리문이 발생하게 된다.

이런 개념으로 jpa에서는 객체의 값을 바꾸면 트랜잭션 commit 시점에 update 쿼리 문이 나가는 것이다.

 

 

 

 

 

- 플러시 

플러시 발생

- 변경 감지

- 수정된 엔티티 쓰기 지연 SQL 저장소에 등록

- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송

 

플러시 호출방법 : flush, commit , JPQL쿼리 실행

 

플러시를 사용하게 된다면, 트랜잭션이 종료될때, 일괄로 처리되는 것이아니라,

그전에 미리 한번 db로 쿼리를 날릴수있다.

예를들어서, 아래와 같은 경우, 쿼리가 ==== 사이에서 나간다.

 

 

 

persist 이후, commit 전에, em.flush를 하게되면, 이렇게 =====전에 쿼리가 발생하게 된다.

 

개인적인 생각으로 대용량 insert 하는 방식에서, 너무 트랜잭션을 오래 물고있거나, 쿼리의 양이 많은 경우에

한번에 처리하려고하면, 에러가 발생하기 마련인데

이렇게 주기적으로 flush를 시켜준다면, 해결가능하니 알아두어야 한다.

flush를 하게되면, sql을 위한, 저장소 즉 쓰기 지연 저장소에 있는 내용만 나가게 되고,

1차캐시는 그대로 남아있다.

 

EnitityManger에서 flush에 대한, 옵션을 줄수도있다.

옵션은 크게 두가지, 디폴트인 auto와, commit을 해야지만 flush가 되는 commitType이있다.

 

flush는 영속성 컨텍스트를 비우는 작업이 아닌 sql쓰기 지연 저장소에 있는 내용을 날려주는 작업이다.

 

 

준영속 상태

영속 상태는, persist와 find를 통한 조회를 했을때 발생한다.

그러면 준영속 상태는? 이와 반대되는 개념이라고 생각하면된다.

em.detach , 아까 업로드했듯이 jpa의 관리 범위에서 제외를 해주는 느낌이다.

-em.detach(entity)

-em.clear() -> 초기화

-em.close() -> 종료

 

초기화와 종료의 차이는, 초기화는 안에 내용이 한번 비워지는 것이고

종료는 그 트랜잭션 내에서는 더이상 영속성 컨텍스트를 사용할수 없다는 것을 의미한다.


(추가)

persist를 하게되면 variable 예

아까 보였듯이 이런식으로 key, value 로 된 HashMap형태로 entitymanger가 가지고있게된다.

 

반응형

'Develop > [JPA]' 카테고리의 다른 글

[JPA] 연관관계 매핑  (0) 2021.07.30
[JPA] Entity Mapping, Table Managing  (0) 2021.07.28
[JPA] 기초 CRUD 사용 해보자  (0) 2021.07.23
[JPA] JPA 기초 개념공부  (0) 2021.07.23
[JPA] JPA N+1 문제란?  (0) 2021.07.12