ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 변경 감지와 병합(merge)
    JPA/JPA 활용 2023. 7. 11. 18:13

    준영속 엔티티

    @PostMapping("items/{itemId}/edit")
    public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") BookForm form) {
    
        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());
    
        itemService.saveItem(book);
        return "redirect:/items";
    }

    book을 보면 JPA에 한 번 갔다온 데이터 객체를 다시 불러온 것이다. JPA가 식별할 수 있는 Id를 가지고있고, 영속성 컨테스트가 더는 관리하지 않는 엔티티를 말한다. Book 객체가 준영속 엔티티이다.

    이미 DB에 한번 저장되어서 식별자가 존재한다고 할지라도 임의로 만들어낸 엔티티도 기존 식별자를 가지고 있으면 준영속 엔티티로 볼 수 있다.

     

    준영속 엔티티의 문제점은 JPA가 관리를 안한다는 것이다. 영속 상태의 entity는 JPA가 관리를 하기 때문에 괜찮은데, 준영속 엔티티는 JPA가 관리하지 않기 때문에 데이터를 변경하기 까다롭다.

     

    단순화해서 set을 많이 사용했지만 실제로는 find.change(price, name, stockQuantity) 등으로 만들어서 사용해야한다. 엔티티의 변경 위치같은 것이 프로젝트에서는 중요하기 때문이다.

     

     

     

     

    준영속 엔티티를 수정하는 방법

    • 변경 감지 기능 사용
    • 병합(merge) 사용

     

     

    1. 변경 감지 기능

    @Transactional
    public void updateItem(Long itemId, String name, int price, int stockQuantity) {
        Item findItem = itemRepository.findOne(itemId);
        // findOne으로 찾아온 findItem은 영속상태이다.
    
        findItem.setName(name);
        findItem.setPrice(price);
        findItem.setStockQuantity(stockQuantity);
    }

    첫 번째로 변경 감지 기능인데, ItemService에서 트랜잭션을 이용해서 값을 수정해주는 방법이다. findOne(itemId)를 이용해서 item 객체를 하나 찾아온다. 이 때 넘어온 item 객체는 영속 상태이다. 그렇기 때문에 set값을 변경해준다 할지라도 스프링의 트랜잭션에 의해서 커밋이 되고, JPA가 set으로 값이 변경된 것들을 찾아 update 쿼리를 날려 데이터를 변경시켜준다.

     

     

    em.persist나 em.update를 실행시키지 않아도 자동으로 해주니까 굳이 persist나 update를 할 필요가 없다.

     

     

     

     

    2. 병합(merge) 사용

     

    병합은 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 방법이다.

    public void save(Item item) {
        if (item.getId() == null) {
            em.persist(item); // 새로 생성된 객체는 persist (신규로 등록)
        } else {
            em.merge(item); // update 비슷한건데 나중에 공부하고 일단 넘어가기
        }
    }

    이전에 ItemRepository에 있던 save 기능이다. id가 null이면 persist하고, 그게 아니라면 merge로 수정을 하는 것이다. 하지만 merge에는 단점이 있어서 사용하는 것을 지양한다.

     

     

     

     

    변경 감지 기능이 일반적으로 병합보다 나은 방법이다.

     

    변경 감지 기능을 사용하면 원하는 속성만 선택해서 변경할 수 있지만, 병합은 모든 속성이 변경된다. 병합시에 파라미터 값이 없으면 null 값이 들어가게 된다.

     

    따라서 merge는 되도록 사용하지 않는 것이 좋다.

     

     

     

    추가적으로 컨트롤러에서 어설프게 엔티티를 사용하면 안된다.

    @PostMapping("items/{itemId}/edit")
        public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") BookForm form) {
    
    //        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());
    //
    //        itemService.saveItem(book);
    
            itemService.updateItem(itemId, form.getName(), form.getPrice(), form.getStockQuantity());
            // book entity를 어설프게 파라미터로 쓰지 않는다.
    
            return "redirect:/items";
        }

    book 엔티티를 맘대로 사용하기 보다는 변경 감지 기능에서 만든 updateItem를 이용해서 사용하는 것이 업데이트를 하기 더 적합하고 좋은 방법이다.

     

     

     

     

     

    데이터 수정 가장 좋은 방법

    1. 컨트롤러에서 어설프게 엔티티 생성하지 않기
    2. 트랜잭션이 있는 서비스 계층에 식별자(id)와 변경할 데이터를 명확하게 전달하기(파라미터나 dto로 전달)
    3. 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경
    4. 트랜잭션 커밋 시점에 변경 감지가 실행됨.
Designed by Tistory.