엔티티를 이용해서 값을 변경(update)할때 아래와 같이 2가지 방법이 존재한다.
이 둘의 차이를 알아보고 어떤것을 쓰는게 더 용이한지 알아보자.
(변경감지가 더 유용합니다.)
public void save(Item item) { //persist해야 Id생김, Id가 없다 -> 완전 새로운 객체
if (item.getId() == null) {
em.persist(item);
} else {
Item merge = em.merge(item); //병합
}
}
파라미터 item 객체는 준영속 엔티티이며 em.merge를 통해
Item merge는 파라미터로 받은 item으로 값들이 다 교체된다.
그 이후, merge된 객체인 'merge'는 영속성 엔티티가 되고 그 이후는 merge 객체를 이용하면 된다.
단, merge를 할 경우 강제적으로 모든 필드의 값들이 교체되며 값이 없으면 null로 들어가기 때문에 조심해야한다.
@Transactional
public Item updateItem(Long itemId, String name, int price, int stock) {
//영속성 객체 가지고 온다.
Item findItem = itemRepository.findOne(itemId);
//원하는 값만 set한다.
findItem.setPrice(price);
findItem.setName(name);
findItem.setStockQuantity(stock);
return findItem;
}
itemId를 통해 item을 가지고 오면 영속성 객체 item이 불러온다.
즉, 영속성 객체 자체에 값을 넣기 때문에 변경사항이 알아서 반영된다.
그렇기때문에 본인이 원하는 값만을 set으로 넣게 되면 @Tranactional이 되면서 em.flush가 될때에 자동적으로 db에 반영이 된다.
merge를 사용하면 강제적으로 모든 필드의 값들이 변경되기때문에 “엔티티를 변경할 때는 항상 변경감지를 사용합니다.”
실제 update를 할때애는 단발성으로 set을 통해서 하는 것이 아니기에 별도의 change나 의미있는 메소드를 만들어서 값을 변경해야한다.
보통 변경포인트를 쉽게 알기 위해 setter들은 막아두고 따로 set메소드를 설정해서 변경포인트를 찾기 쉽게 합니다.
@PostMapping("/items/{itemId}/edit")
public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") BookForm form) {
// 이렇게 book 객체를 만들어서 넣지 않는다.
/* Book book = new Book();
book.setId(form.getId());
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());*/
// @Service에서 메소드를 만들고 Controller는 단순히 메소드를 사용하는 장소
itemService.updateItem(itemId, form.getName(), form.getPrice(), form.getStockQuantity());
return "redirect:/items";
}
아이디 혹은 변경할 정보들을 정확히 전달하기
Controller에서 식별자만 넘기고 핵심 비즈니스 로직은 @Transactional(@Service)안에서 처리하는 게 좋다.
[출처: 인프런, 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발, 김영한]