준영속 엔티티
- 영속성 컨텍스트가 더이상 관리하지 않는 엔티티
준영속 엔티티를 수정하는 방법
- 변경감지 기능 사용(Dirty Checking)
- 병합(merge) 사용
변경감지기능 사용(itemService에서 작성)
/**
* 변경감지
*/
@Transactional
public void updateItem(Long itemId, Book param){
Item findItem = itemRepository.findOne(itemId);
findItem.setName(param.getName());
findItem.setPrice(param.getPrice());
findItem.setStockQuantity(param.getStockQuantity());
}
여기서 itemRepository.save 를 호출할 필요가 없다. ( findItem은 DB에서 가져온 영속 상태 값 )
왜냐하면 @Transactional이 실행됨과 동시에 commit이 되면서 flush가 날라간다.
flush 되면서 수정된 부분을 jpa가 확인하고 그 부분을 알아서 DB에 반영함(update 쿼리 날림)
병합 사용
- 준영속 상태를 영속 상태로 변경할 때 사용
public void save(Item item){
if(item.getId() == null){
em.persist(item);
}else{
Item merge = em.merge(item); // update 비슷한 것?
// merge는 받아온 파라미터로 모두 교체해버린다. 병합이 되면 merge 값은 영속성 컨텐스트에서 관리가 되며 이 값을 사용해야한다.
// but item에 있는 값중에 null이 있다면 null도 바꿔치기 해버린다 그래서 사용하면 X -> 위험
}
}
병합 동작 방식
- merge() 실행
- 파라미터로 넘어온 준영속 엔티티 식별자 값으로 1차 캐시에서 엔티티 조회 (이때 1차 캐시에 없으면 DB 조회 후 1차 캐시에 저장)
- 조회한 영속 엔티티 merge에 item 엔티티 값을 채워넣음( item의 모든 값을 merge에 채워넣음 )
- 영속 상태인 merge를 반환함
이걸 변경감지 코드로 풀어내면 아래와 같다.
/**
* merge의 동작 방식
*/
@Transactional
public Item mergeItem(Long itemId, Book param){
Item findItem = itemRepository.findOne(itemId);
findItem.setName(param.getName());
findItem.setPrice(param.getPrice());
findItem.setStockQuantity(param.getStockQuantity());
return findItem;
}
★ 병합 사용 시 주의 할 점
: 변경감지 기능을 사용하면 원하는 속성만 선택해서 변경가능하지만, 병합은 모든 속성이 변경 된다. 이때 병합 시 값이 없으면 null로 업데이트 할 위험이 있다.
→ 병합은 웬만하면 그냥 사용하지 말 것!
가장 좋은 해결 방법
엔티티를 변경할 때는 항상 변경 감지 사용
참고사항
- 컨트롤러에서 어설프게 엔티티 생성하지 말라.
- 트랜잭션이 있는 서비스 계층에 식별자와 변경할 데이터 명확하게 전달
- 서비스 계층에서 영속상태 엔티티를 조회하고, 엔티티의 데이터 직접 변경
- 커밋시점에 변경 감지 실행됨
어설프게 엔티티를 생성 한 예
아래 처럼 수정하는게 훨씬 깔끔하다.
< controller >
@PostMapping("/items/{itemId}/edit")
public String updateItem(@PathVariable("itemId") Long itemId, @ModelAttribute("form") BookForm form){
itemService.updateItem(form.getId(), form.getName(), form.getPrice());
return "redirect:/items";
}
<service>
/**
* 변경감지
*/
@Transactional
public void updateItem(Long itemId, String name, int price){
Item findItem = itemRepository.findOne(itemId);
findItem.setName(name);
findItem.setPrice(price);
}
'JAVA' 카테고리의 다른 글
[JAVA] @Query에서 DTO로 조회하기 (0) | 2022.02.15 |
---|---|
[JAVA] @NamedQuery 와 @Query (0) | 2022.02.15 |
[JAVA] AllArgsConstructor vs RequiredArgsConstructor (0) | 2022.02.10 |
[JAVA] 쿼리 파라미터 남기기 (0) | 2022.01.20 |
[JAVA] fetch join (0) | 2022.01.18 |