JPA

JPA - JPA의 수행 순서와 Entity의 생명주기, Context의 특징 실험.

비뀨_ 2022. 6. 26. 03:15

 

 

기본적인 영속성의 개념은 지난 글에 적어 놓았다.

https://beetr.tistory.com/59

 

JPA의 영속성 기본 개념 & 엔티티 매니저

영속성 ( Persistence ) 영속성이란 사전의 단어대로 영원히 존재하는 속성이다. 보통 데이터베이스(DB)를 접해봤다면 한번 쯤은 들어봤을 말이다. JPA에서는 영속성 컨텍스트라는 개념이 중요하다.

beetr.tistory.com

 

JPA는 객체와 관계형 데이터베이스의 데이터를 자동으로 연결해주는 ORM이다.

그럼 그 일을 누가 해줄까?

엔티티 매니저(EntityManager)가 영속성 컨텍스트( Persistence Context )에 저장한다.

 

대략적으로 이해한 부분을 기억하기 쉽게 그림으로 표현해 봤다.

 

  1. 팩토리는 엔티티 매니저를 고용한다. (만든다.)
  2. 매니저는 엔티티를 영속성 컨텍스트에 담는다.
  3. 컨텍스트는 짐 ( 엔티티 )이 실릴 때까지 기다렸다가 DB에 운반해준다.

설명한대로 Entity는 DB에 저장되기 전까지 영속성 컨텍스트에 저장되어 있다.

영속성 컨텍스트에 저장된 Entity는 생명주기를 갖고 있다.

 

Entity의 생명주기

비영속

쉽게 말하자면 아직 컨텍스트에 싣지 않은 상태이다.

' 안 불렀는데 어떻게 가요... '

다시 말해 위와 같이 그냥 JPA랑 연결지점이 없는 상태라고 생각해도 괜찮을 것 같다.

 

위의 Member의 객체를 생성하고 값만 넣었다면 그냥 값을 넣은 객체가 되지 Context와는 아무 상관이 없는게 된다.

 

영속

컨텍스트에  저장되어 컨텍스트의 관리를 받게 되는 상태를 말한다.

 

위처럼 manager.persis(member)를 통해 컨텍스트에 올라간 상태를 말한다.

( = 컨텍스트 화물차에 엔티티라는 짐이 실린 상태 ) 

-- return null은 나중에 할거라 일단 null 처리 했다.--

 

단순히 persist 외에도 find 나 JPQL을 통해서 조회된 상태도 영속 상태이다.

 

준영속

짐을 실은 상태에서 짐을 다시 내리는게 준 영속 상태라고 한다.

 

'어...? 그럼 영속성이 없는거니까 생명주기에서 삭제된거 아니야?' 라고 할 수도 있는데

준영속 상태는 컨텍스트에서만 Entity를 뺀 것이라고 한다.

 

삭제

컨텍스트에서 삭제하고, DB에서도 삭제하는게 Entity가 삭제되었다라고 한다.

 

 

영속성 컨텍스트 특징

 

  • 식별자와 값
    • 영속성 컨테스트는 엔티티를 식별자와 값으로 구분한다.
    • 때문에 Entity는 식별할 수 있는 Id를 반드시 가지고 있어야한다.
  • DB 저장
    • 컨텍스트는 DB에 저장을 트랜잭션이 커밋 될 때 한다. ( flush : 한번에 붓다. )
  • 기타
    • 1차 캐시가 있다.
    • 동일성 보장 ( == )
    • 쓰기 지연 
    • 변경 감지
    • 지연 로딩

1차 캐시

영속성 컨텍스트는 내부에 캐시를 갖고 있어서 영속 상태의 엔티티는 캐시에 저장되는데,

키가 Id이고 값은 Entity로 저장 된다.

 

영속 상태에 있는 Entity를 조회할 때에는 DB를 통해 조회하지 않고 캐시에서 조회하기 때문에 빠르다.

 

동일성 보장

1차 캐시를 참고하면 이해가 쉬워진다.

캐시에 있는 데이터를 찾아온다.

한번 실험해 보자.

 

...? 오잉 뭐야 동일성 보장한다고 하지 않았나..?

라고 생각할 수도 있는데, 같은 트랜잭션 안에 있을 때 동일성을 보장한다는거다.


 

예를 들어서  오픈기념으로 처음으로 방문한 고객에게 구매시 할인을 30프로 해준다고 가정해보자.

한 사람이 물건을 구매하고 나왔는데 까먹은 물건이 있어서 다시 구매하려고 했지만,

점원이 '구매 이력이 있으시네요. 할인 적용이 안되세요.' 라고 하는 것과 같은거다.


 

위의 MemberDto는 이미 가게를 나온 후(트랜잭션 종료 후) 다시 들어갔기 때문에

트랜잭션은 그 가게를 들렀다가 나올 때까지의 기간이라고 보면 이해하기 쉬울 것 같다.

트랜잭션에 대한 설명은 자세한건 다른 블로그들도 많으니 참고!

 

그럼 다시 돌아가서 이번엔 한 트랜잭션에서 해보자.

아까처럼 Test 파일에서 두번 호출하지 않고 memberService에서 메서드 두개를 만들었다.

 

Test 파일에서 한번만 호출

 

 

위처럼 하면

쨔잔 ~ true가 나온다.

 

Debug 모드로 찍어본 결과 findMember1과 2는 Member@9986을 바라보고 있기 때문에 true가 된다.

 

 

* 아래처럼 꼭 transacion을 안써도 같은 결과가 나오긴 한다. JPA로 일처리를 하면 컨텍스트의 캐시를 사용하기 때문인걸까..? 조금 더 공부를 해봐야 알 것 같지만 JPA의 핵심이 컨텍스트라고 했으니까 맞을 것 같다.

 

쓰기 지연

쓰기 지연은 말 그대로 ( 데이터베이스에) 쓰는 걸 미룬다는것이다.

그냥 설명말고 바로 실험해보도록 하겠다.

 

다시 디버그 모드로 실험 해봤다.

manager.persist()를 두번 호출했고 insert 쿼리가 두번 만들어졌다 ( 위에 짤림..) 

근데 DB 툴을 계속 새로고침해도 데이터가 추가되지 않았다. ( 지금 진행은 member2까지 진행된 트랜잭션 커밋 전)

 

아까 위 사진처럼 말한대로면  컨텍스트가 flush 하는 시점인 commit을 호출하고 나면 되어야 한다.

 

commit을 수행하고 나니 데이터가 생겼다 'ㅅ' !!  ( male을 mail 로 오타난건 실수....)

 

아까 말 한 대로 컨텍스트는 DB에 저장을 커밋 될 때 한다는게 증명이 됐다.

 

 

 

요롷게 쿼리를 만들고 캐시에 저장과 SQL 저장소에 차곡차곡 모아놨다가

끝나면 flush ( 들이부음) 하는 것이다.

( persist만 이렇게 되는게 아니라 Entity 조회, 수정, 등록 , 삭제 등 모두 이런식으로 동작)

 

변경 감지

update를 한다고 할 때 너무나 당연하게 update 구문을 짜야 update를 하는거아냐?라고 생각 할 수 있다.

하지만 JPA가 알아서 해준다고 한다.

 

이번에는 3번을 찾기만하고 객체 자체에 이름만 고길동으로 변경해줬다.

3번의 이름은 김개똥3이다.

이건 Debug 안찍고 그냥 돌려봤다.

근데 console에 찍힌 로그를 보니까 나는...업데이트 한 적이 없는데 왜 update 구문이 생겼지...?

3번의 이름은 왜 고길동으로 바뀌었고...?

 

간단히 말하자면 컨텍스트에 보관할 때 상태를 복사해서 저장해두는데, 

상태가 변경이 되면 처음 저장할 때의 값이랑 달라질거니까 변경이 된 부분을  또 SQL 구문을 짜서 SQL 쓰기 저장소에 집어넣고 트랜잭션 커밋될 때 수정사항을 보낸다.

 

변경감지는 영속 상태의 Entity만 가능하다.

 

지연로딩은 말그대로 쓸 때 만들겠다라는 얘기다. 너무 길어질 것 같아서 짧게만 말하고 정리하겠다.

'JPA' 카테고리의 다른 글

JPA의 영속성 기본 개념 & 엔티티 매니저  (0) 2022.06.23