[JPA] 병합(merge) VS 변경감지(dirty checking)

jiaLEE·2022년 6월 24일
0

엔티티를 이용해서 값을 변경(update)할때 아래와 같이 2가지 방법이 존재한다.
이 둘의 차이를 알아보고 어떤것을 쓰는게 더 용이한지 알아보자.
(변경감지가 더 유용합니다.)

  • 준영속 엔티티란?
    영속성 엔티티가 더이상 관리하지 않는 엔티티이며 이미 한번 persist되어서 식별자가 있는 경우를 뜻한다. 값이 변경되어도 알아서 반영이 되지않기 때문에 따로 값을 반영해야한다.

병합(merge)

 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로 들어가기 때문에 조심해야한다.

변경 감지(Dirty Checking)

@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메소드를 설정해서 변경포인트를 찾기 쉽게 합니다.

  • Controller에서 어설프게 엔티티 생성하지 말기
    web계층용 객체를 따로 만들어서 쓰기로 했기때문에
    Controller에서 book 객체를 생성해서 값을 넣는 것이 아니라
    update하는 메소드를 @Service에서 만들고 Controller는 단순히 그 메소드를 불러오는 식으로 코드를 짠다.
 @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 - 웹 애플리케이션 개발, 김영한]

0개의 댓글