# 영속성 컨텍스트
엔티티가 영속상태로 관리가 되면 , 거기있는 값만 바꾸면 JPA가 트랜젝션 commit 시점에 flush하여 변경된 내용을 DB에 반영해줌.
Book을 가져온 후 트랜젝션 안에서는 setName으로 이름 바꾼 후 트랜젝션이 commit되면 JPA가 변경분을 자동으로 찾아서 UPDATE 쿼리를 자동생성해서 DB에 반영함 -> 변경감지 = dirty checking ( JPA의 기능 )
예를 들면
여기서 처럼 Order의 Status 를 바꿔 줬음에도 불구 하고 따로 DB에 UPDATE 쿼리를 날리는 코드는 없다.(em.update 나 em.merge 없었다. )
-> 영속상태인 엔티티의 값을 바꿔놓으면 JPA가 트랜잭션 커밋 시점에 변경감지하여 DB에 UPDATE문 날리고 트랜젝션 커밋함.
-> 값을 바꿔놓으면 JPA가 트랜잭션 커밋 시점에 변경감지하여 DB에 UPDATE문 날리고 트랜젝션 커밋함.
====================================================== 준영속 엔티티
예를 들면 여기서의 Book 객체가 객체는 새로운 객체인데 Id가 세팅이 되어있다. -> JPA에 한 번은 들어갔다 나온 객체이다 -> 위의 Book 객체는 준영속 엔티티.
( 식별자가 정확히 DB에 있으면 준영속 엔티티라고 함. ) = 영속성 컨텍스트가 더는 관리 하지 않는 엔티티.=JPA가 더는 관리 안함
준영속 엔티티(Book)의 문제점 : JPA가 관리 안함.
-> JPA가 관리하는 영속상태 엔티티는 변경감지가 일어남. ( JPA가 보고있음 ) -> 그래서 트랜젝션 커밋 시점 직전에 변경된 것을 바꿔줌.
일단 여기서 Book 객체는 내가 new해서 만든것이므로 JPA가 관리 안함. -> 아무리 Book에다가 값을 바꿔도 DB에 UPDATE가 안 일어남. ( Transaction 이 있다해도 JPA가 관리 안하기 떄문에 자동으로 UPDATE 해줄 수도 없다. )
<이 부분 다시 듣자. 이해가 안된다.>
=> 그렇다면 이런 준영속 엔티티의 데이터를 변경할 수 있을까?
https://www.inflearn.com/questions/70393
book 객체가 왜 준영속인것인가 - 인프런 | 질문 & 답변
[사진] 수정을 시도하는 Book객체는 새로 만들어진것이잖아요 강사님. 이 Book객체는 이미 DB에 한번 저장이 되었다고 했는데 persist를 한적도 없고 db를 통해 find 한 객체도 아닌데 어떻게 이게 준영
www.inflearn.com
1.
findOne으로 (em.find) 찾아온 객체는 영속상태이다. -> 리턴 된 findItem도 영속상태객체를 가리킨다.
29. itemId를 기반으로 실제 DB의 영속상태의 Item을 찾아옴.
findItem들 값 세팅
33. 여기서 findItem을 persist 해줘야 할까???????? => 필요없다.
findItem은 영속상태이다. -> 메서드가 끝나면 스프링의 @Transactional 에 의해서 열렸던 트랜젝션이 commit이 된다. commit이 되면 JPA는 flush를 한다. (flush : 영속성컨텍스트중에서 변경된 애들을 찾는다. ) -> 찾으면 바뀐 부분을 UPDATE 쿼리를 DB에 날려서 업데이트한다. ( 이게 맞음 )
이제 보인 건데 Service 클래스에는 항상 @Transactional 이 붙어있다. ( 클래스단위에 readOnly true 해주자. 값을 변경하는 메서드는 따로 @Transactional 붙여줌. ) -> Repository에 접근하는 계층이라 @Transactional 붙여줌.
1. Merge를 이용한 update
id가 있으면 em.merge(item)을 한다.
param으로 넘어온 Book객체로 findItem을 찾고 JPA가 findItem 에 param의 모든 데이터를 바꿔치기해줌 -> 그 뒤 트랜젝션 커밋 될 때 변경감지기능에 이 부분이 걸려서 다 UPDATE 반영이된다.
바꿔치기 된 객체를 반환해줌. ( 여기선 리턴 없음 ) -> JPA가 만들어 주는 것에는 있다.
바로 위의 saveItem을 참고하자면 save에 넘긴 item 이 em.merge(item) 될 때 JPA가 모든 데이터를 바꿔치기해줌.
이걸 보면 바꿔치기 된 객체를 반환해준다는 것을 알 수 있다. -> merge는 영속성 컨텍스트에서 관리되는 객체, 그러나 파라미터 item은 영속상태로 변하진 않음. ( 서로 다른 애 )
# 정리
이 사진 다시보면 이해될듯.
위의 1,2,3번
1. 준영속 엔티티의 식별자 값으로 영속엔티티를 조회한다.
Item findItem= itemRepository.findOne(param.getId());
2. 영속 엔티티의 값을 준영속엔티티의 값으로 모두 교체한다.(병합한다.)
findItem.set 3줄
3. 트랜잭션 커밋 시점에 변경감지기능이 동작해서 DB에 UPDATE 쿼리를 보낸다.
# 문제점.
null로 업데이트 될 수 있다.!!!!!!!!!! 매우 위험.
=> 이래서 merge 안쓰고 변경감지 쓴다. ( 실무에서도 )
=> 직접 영속객체를 조회한 후 업데이트 할 필드를 set, set, set.. 해서 반환해야함.
이런식으로.
-> 여기서 중요한 점 : ItemService에서 setsetset 하지말고 메서드로 뽑아서 Item 엔티티에 넣어두자. ( 변경지점이 엔티티로 가서 좋음. ( 나중에 고칠 떄 ))
-> 변경사항은 엔티티에서 볼 수 있게
번외 :: 우리가 만든 BookForm 객체는 웹 계층에서만 쓰자. ( Controller, resources/templates/ )
그래서 ItemService에서 Book param 으로 받음.
# 결론
1. 이렇게 넘겨주면 유지보수 훨씬 쉬움. ( 컨트롤러에서 어설프게 엔티티 생성안함. -> 변경감지 사용. )
2. ItemService에서 식별자와 변경할 데이터를 파라미터로 받음. ( 아니면 UpdateItemDto 만들어서 name, price, stockQuantity 줘도됨. )
# 오늘의 결론 1번
김영한님 답변 :
트랜잭션을 커밋하게 되면 내부에서 자동으로 플러시가 호출됩니다.
그리고 플러시가 일어나야 변경감지가 동작합니다.
***********트랜젝션 안에서 엔티티를 조회해야 영속상태로 조회 된다. 거기에 값을 변경해야 dirty checking 이 일어난다. -> 그러면 트랜젝션이 커밋 될 때 flush가 일어나면서 변경감지된 게 UPDATE쿼리가 DB에 쫙 나간다.
# 오늘의 결론 2번
-> 여기서 중요한 점 : ItemService에서 setsetset 하지말고 메서드로 뽑아서 Item 엔티티에 넣어두자. ( 변경지점이 엔티티로 가서 좋음. ( 나중에 고칠 떄 ))
-> 엔티티 안에서 바로 추적할 수 있는 메서드를 만들어라. 예를 들면 위의 Item엔티티클래스의 change메서드
-> 이러면 모든 변경 사항을 모아두게 되므로 change만 뒤져도 뭘 바꾸는지 알 수 있다.
'Java, Spring > 스프링부트와 JPA 활용 1' 카테고리의 다른 글
@Transactional 과 영속성 컨텍스트 (0) | 2022.08.14 |
---|---|
7-8. 상품 주문 ~ 주문 목록 검색( 중간부분 아주중요 ) (0) | 2022.08.14 |
7-4~7-6. 상품등록, 상품목록, 상품수정(중요) (0) | 2022.08.13 |
7. 레이아웃, 회원가입, 회원조회(중요) (0) | 2022.08.13 |
6-5. 주문 검색 기능 개발 (0) | 2022.08.13 |