본문 바로가기

CS 잡지식

JPA [수정(update)]의 2가지 전략(feat. dirty checking, 병합(merge) )

JPA의 [준영속 엔티티]에 대한 [수정(update)] 전략은 2가지가 있다. (영속 엔티티라도 전략은 같음)

준영속 엔티티 : Context가 더 이상 관리하지 않는 엔티티.

-> 아래의 코드에서 보듯이, 임의로 만든 엔티티여도 [기존 식별자]를 가지고 있으면, [준영속] 엔티티라고 볼 수가 있다.

특징은, Context에서 관리되었던 엔티티의 [식별자]를 가지고 있다는 것!

1] 변경 탐지(dirty checking)

2] 병합(merge) : 절대로 사용하면 안 됨(이유는 아래에서 천천히 설명을 함)

-> JPA는 변경 탐지(Dirty Checking)을 [수정]의 전략으로 권장하는 [수정]의 Best Practice이며, 

강사 왈 "병합(merge)는 실무에서 거의 사용하지 않는다" 라고 하심. 

 

- Dirty Checking과 Merge의 차이점 - 

( 이 둘의 개념과 차이점을 모르면, 개발 중 엄청나게 고생을 할 거라고 강조 또 강조를 하셨다. 꼭 숙지하자)

@PostMapping(value = "/items/{itemId}/edit")  // [상품 목록] -> [수정] -> [수정] 페이지로부터 데이터를 받아서 업데이트!
public String updateItem(@PathVariable String itemId,@ModelAttribute("form") BookForm form) {

    Book book = new Book();
    book.setId(form.getId()); // 기존 [식별자]를 가지고 있으므로, 임의로 만든 book이지만 [준영속] 상태의 엔티티로 볼 수 있다(ppt 89)
    book.setName(form.getName());
    book.setPrice(form.getPrice()); 
    book.setStockQuantity(form.getStockQuantity());
    book.setAuthor(form.getAuthor());
    book.setIsbn(form.getIsbn());

    itemService.saveItem(book); //  [수정](여기서는 [병합] 전략으로 수정하도록 코드를 짜놓음)

    return "redirect:/items"; // [수정]이 완료되면, redirect
}

ItemService.saveItem()이 어떻게 정의돼 있나 확인을 해보자

@Transactional // readOnly == false
public void saveItem(Item item){

    itemRepository.save(item);

}

이번에는 itemRepository.save()가 어떻게 정의돼 있는지 확인을 해보자. 

public void save(Item item){

    if(item.getId() == null){ // 해당 Item 객체는 [준영속]도 [영속] 엔티티도 아니다. 
        entityManager.persist(item);
    }
    else{ // 헤당 Item 객체는 [준영속] 엔티티이다. 
        entityManager.merge(item); 
    }
}

 

 entityManager.merge(item)은 아래의 코드의 똑같이 동작을 한다. 

@Transactional
public Item updateItem(Long itemId, Book book){ // book : [준영속] 엔티티, itemId : book의 [기존 식별자]

    Item findITtem = itemRepository.findOne(itemId); // Context or DB에서 Item(Book) 가져옴.즉, findItem은 [영속] 엔티티이다.
    findITtem.setPrice(book.getPrice());
    findITtem.setName(book.getName());
    findITtem.setStockQuantity(book.getStockQuantity());
    
    return findItem;

    //findItem은 Context에 의해 관리가 되고 있는 [영속] 엔티티이므로, 결과적으로는 [Dirty Checking]으로 [Merge]가 동작을 하여, 최종적으로 [수정]이 일어난다.  
}