변경 감지와 병합

mminjg·2022년 3월 29일
0

준영속 엔티티란?

영속성 컨텍스트가 더는 관리하지 않는 엔티티이다.
준영속 엔티티는 JPA가 관리하지 않기 때문에 값을 변경해도 DB에 업데이트가 일어나지 않는다.

@PostMapping(value = "/items/{itemId}/edit")
public String updateItem(@ModelAttribute("form") BookForm form, @PathVariable String itemId) {

    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객체는 이미 DB에 저장되었다가 나온 객체여서 식별자 id가 존재한다.
  • 기존 식별자를 가지고 있으면 준영속 엔티티로 볼 수 있다.

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

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

변경 감지 기능

@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
 Item findItem = em.find(Item.class, itemParam.getId()); //같은 엔티티를 조회한다.
 findItem.setPrice(itemParam.getPrice()); //데이터를 수정한다.
}
  • 영속성 컨텍스트에서 엔티티를 다시 조회하여 데이터를 수정한다.
  • 트랜잭션 커밋 시점에 변경 감지(Dirty Checking)이 동작하게 되어 DB에 UPDATE SQL이 실행된다.

병합 사용

@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
	Item mergeItem = em.merge(item);
}
  • 파라미터로 넘어온 item은 영속성을 가지지 않으며 mergeItem이 영속성 컨텍스트에서 관리된다.

병합 동작 방식

  1. merge() 를 실행한다.
  2. 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회한다.
    2-1. 만약 1차 캐시에 엔티티가 없으면 데이터베이스에서 엔티티를 조회하고, 1차 캐시에 저장한다.
  3. 조회한 영속 엔티티(mergeMember)에 member 엔티티의 값을 채워 넣는다. (member 엔티티의 모든 값을 mergeMember에 밀어 넣는다. 이때 mergeMember의 “회원1”이라는 이름이 “회원명변경”으로 바뀐다.)
  4. 영속 상태인 mergeMember를 반환한다.

병합시 동작 방식 정리

  1. 준영속 엔티티의 식별자 값으로 영속 엔티티를 조회한다.
  2. 영속 엔티티의 값을 준영속 엔티티의 값으로 모두 교체한다.(병합한다.)
  3. 트랜잭션 커밋 시점에 변경 감지 기능이 동작해서 데이터베이스에 UPDATE SQL이 실행한다.

🧨주의

  • 병합 동작 방식은 모든 필드를 교체(병합)한다.
  • 병합시 값이 없으면 null로 업데이트하게 된다.

어떤 방법을 사용해야할까?

항상 변경 감지를 사용하자!

  • 컨트롤러에서 엔티티 생성x
  • 트랜잭션이 있는 서비스 계층에 식별자( id )와 변경할 데이터를 명확하게 전달(파라미터 or Dto)
  • 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경
  • 트랜잭션 커밋 시점에 변경 감지가 실행

참조

inflearn JPA 활용1

0개의 댓글