준영속 엔티티
영속 상태이면 그 엔티티의 값만 바꾸면 JPA가 커밋 시점에 값을 바꿔준다.
하지만 준영속 엔티티일 때는 문제가 생긴다. 준영속 엔티티는 영속성 콘텍스트가 더는 관리하지 않는 엔티티를 말한다.
우리 예제에서는 itemService.saveItem(book)에서 수정을 시도하는 Book객체다. 수정을 할 때는 DB에 한번 저장되어 있던 것을 다시 꺼내와 임의로 엔티티로 다시 만드는 것이다. 이렇게 DB에서 저장되었다가 다시 꺼내온 엔티티를 준영속 엔티티로 볼 수 있다.(임의로 만들어낸 엔티티도 기존 식별자를 가지고 있는 경우 = id를 가지고 있는경우)
준영속 엔티티를 수정하는 2가지 방법
- 변경 감지 기능 사용
//ItemService
@Transactional
public void updateItem(Long itemId, Book param){
Item findItem = itemRepository.findOne(itemId); //영속상태
findItem.setName(param.getName());
findItem.setPrice(param.getPrice());
findItem.setStockQuantity(param.getStockQuantity());
findItem.setCategories(param.getCategories());
}
영속성 컨텍스트에서 엔티티를 다시 조회한 후에 데이터를 수정하는 방법이다.
조회한 엔티티는 영속상태이기 때문에 엔티티의 내용을 변경하면 모든 내용이 변경된다.
- 트랜잭션 안에서 엔티티를 다시 조회한다.
- 변경할 값 선택 트랜잭션 커밋 시점에 변경 감지(Dirty Checking) 이 동작해서 데이터베이스에 UPDATE SQL 실행
- 병합 사용
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item mergeItem = em.merge(itemParam);
}
병합은 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용이 가능하다. 위에 짠 요소들을 merge가 한번에 다 바꿔준다.
Merge 순서

- merge()를 실행한다.
- 파리미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회한다.
- 만약 1차 캐시에 엔티티가 없으면 DB에서 조회한다.
- 조회한 영속 엔티티에 member엔티티의 값을 채워 넣는다.
- 영속 상태인 mergeMember를 반환한다.
Merge의 순서를 보니 변경 감지 기능사용과 똑같은 방식으로 값을 업데이트 하는 것을 알 수 있었다.
주의할 점
변경 감지 기능을 사용하면 원하는 속성만 선택해서 변경할 수 있지만, 병합을 사용하면 모든 속성이 변경된다. 병합시 값이 없으면 null로 업데이트 할 위험도 있다. 실무에서 변경 감지가 더 안전하다!!
강사님이 추천하는 방법
엔티티를 변경할 때는 항상 변경 감지를 사용하자!
- 컨트롤러에서 어설프게 엔티티를 생성하지 말자.
- 트랜잭션이 있는 서비스 계층에 식별자( id )와 변경할 데이터를 명확하게 전달하자.(파라미터 or dto)
- 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경하자.
- 트랜잭션 커밋 시점에 변경 감지가 실행된다.
//ItemService
@Transactional
public void updateItem(Long id, String name, int price, int stockQuantity)
{
Item item = itemRepository.findOne(id);
item.setName(name);
item.setPrice(price);
item.setStockQuantity(stockQuantity);
}
//ItemController
@PostMapping("items/{itemId}/edit")
public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") BookForm form){
itemService.updateItem(itemId, form.getName(), form.getPrice(), form.getStockQuantity());
return "redirect:/items";
}
변경 감지 기능을 사용하는 것으로 코드를 변경했다.
강의 출저
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 인프런 | 강의
실무에 가까운 예제로, 스프링 부트와 JPA를 활용해서 웹 애플리케이션을 설계하고 개발합니다. 이 과정을 통해 스프링 부트와 JPA를 실무에서 어떻게 활용해야 하는지 이해할 수 있습니다., - 강
www.inflearn.com
'BackEnd > 실전! 스프링 부트와 JPA 활용1' 카테고리의 다른 글
웹 계층 개발(상품) (1) | 2023.01.06 |
---|---|
웹 계층 개발(회원) (0) | 2023.01.06 |
주문 도메인 개발 (0) | 2023.01.05 |
상품 도메인 개발 (0) | 2023.01.04 |
회원 도메인 개발 (0) | 2023.01.04 |