변경 감지 동작 원리
변경 내용을 JPA가 자동으로 인식해 commit할 때 반영해줌.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//!!!!! em.update(member) 이런 코드가 있어야 하지 않을까?
transaction.commit(); // [트랜잭션] 커밋
!! em.update는 필요하지 않다.
commit시 로직
1. 영속 컨텍스트에 객체가 들어오면 바로 스냅샷 찍어두는데 commit할 때 내부적으로 flush호출하고 스냅샷과 엔티티 일일히 다 비교한다.
2. 변경사항 생기면 쓰기 지연 SQL저장소에 update 쿼리를 만든다.
3. update 쿼리를 DB에 반영하고 commit 한다.
*remove도 동일 (update 쿼리가 아닌 delete 쿼리 만든다.)
영속상태 DB에 반영하기 (변경감지 이용)
엔티티가 영속상태면 데이터가 변경될때 jpa가 커밋(정확히는 flush)시점에 알아서 변경감지를 해 변경된 데이터를 DB에 반영해줌.
Order의 cancel 메서드보면 알아서 DB 반영을 해주기 때문에 this.setState(OrderStatus.CANCEL); 만 해두고 em.persist나 em.merge안 함.
--------------
새로운 빈 껍데기 Book book = new Book()으로 만들어진 객체지만 book.setId(form.getId())로 id가 이미 채워진것 (@GeneratedValue로 인해서 id가 만들어지지 않은 인스턴스)은 준영속상태임 = merge(원래 스냅샷과 달라진 것을 찾아서 update. 아래 1번과 같은 코드를 jpa가 실행해줌.)해줘야함.
준영속상태 DB에 반영하기
1. 변경 감지
@Transactional 안에서 findOne(id)(이건 id를 이용해 영속상태인걸 찾아온것)해서 findItem을 만들고 파라매터로 데이터를 받아 findItem.set~으로 이미 영속상태의 findItem의 값들을 바꿔치기 해줌. Transactional 끝나면 commit되고 그럼 jpa가 알아서 flush(변경된 게 있나찾기)해 DB에 반영시켜줌.
2. merge 원리
- 준영속 엔티티의 식별자 값으로 영속 엔티티를 조회한다.
- 영속 엔티티의 값을 준영속 엔티티의 값으로 모두 교체한다.(병합한다.)
- 트랜잭션 커밋 시점에 변경 감지 기능이 동작해서 데이터베이스에 UPDATE SQL이 실행
merge(member)하면 member id로 1차캐시에서 찾고 없으면 DB에서 가져옴. 가져온게 memberResult면 member값들을 memberResult.set~(member.get~())으로 memberResult에 채워넣어줌. 그리고 memberResult반환
- member는 영속성 컨텍스트에 들어가지 않고 반환되는 memberResult만 영속성 컨텍스트에 의해 계속 관리됨.
주의: merge는 모든 값을 변경시킨다. 병합할 값이 null이면 null로 업데이트 되기 때문에 merge보다 원하는 속성만 선택해 변경시킬 수 있는 1번 방법(변경 감지 기능)이 안전
=> 실무에서는 보통 변경가능한 데이터만 노출하기 때문에, 병합을 사용하는 것이 오히려 번거롭다.
핵심! 가장 좋은 방법
엔티티를 변경할 때는 항상 변경 감지를 사용하세요.
※ 아래는 다 같은말이며 사진을 참고하세요.
- 컨트롤러에서 어설프게 엔티티를 생성하지 마세요.
- 트랜잭션이 있는 서비스 계층에 식별자( id )와 변경할 데이터를 명확하게 전달하세요.(파라미터 or dto)
- 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경하세요.
- 트랜잭션 커밋 시점에 변경 감지가 실행됩니다.
- Controller는 이정도까지만
- Service에서 진짜로 생성 및 변경하기
'Spring boot' 카테고리의 다른 글
[spring boot] 3차 정리 (페치 조인, 벌크 연산) (0) | 2022.01.21 |
---|---|
[spring boot] 3차 정리 (값 타입) (0) | 2022.01.21 |
[spring boot] 영속성 전이 (cascade)와 orphanRemoval 차이 (0) | 2022.01.20 |
[spring boot] 실전! (0) | 2022.01.10 |
[spring boot] 각 join 특징 (+ on, where 차이) (0) | 2021.12.27 |