이 글은 김영한 님의 실전! 스프링 부트와 JPA 활용-1 강의를 참고하여 작성한 글입니다.
스프링에서 EntityManger
의 주요 기능 중 하나로 Dirty Check
가 있다.
EntityManger
는 현재 트랜잭션에서 영속성 컨텍스트에 포함되어 있는 Entity
객체에 수정이 발생할 경우, 트랜잭션이 커밋되는 시점에 내부적으로 UPDATE
쿼리를 생성하여 전달한다.
기본적으로 EntityManager
의 find()
나 createQuery()
메서드를 통해 조회는 Entity
객체는 영속성 컨텍스트에 포함된다. 따라서, persist()
메서드를 명시적으로 호출하지 않아도 된다.
그러나, EntityManager
는 준영속 상태(Detached
)의 Entity
에 대해서는 Dirty Check
를 수행하지 않는다. 만약, 사용자가 폼 정보를 기반으로 새로운 Entity
객체를 생성한다면 해당 객체는 준영속 상태에 해당되어 Dirty Check
가 수행되지 않는다.
그러므로 준영속 상태에 놓인 Entity
객체를 수정하기 위해서는 EntityManager
에서 find()
나 createQuery()
를 호출하여 영속 상태의 객체를 불러와 데이터를 수정하거나, 해당 Entity
를 merge
하여 영속 상태로 변경하여 Dirty Check
가 작동하도록 해야한다.
merge()
는 준영속 상태에 있는 Entity
를 영속성 컨텍스트에 다시 편입시키는 메서드이다. merge()
를 호출하면 Entity
가 영속 상태가 되며, 변경 사항 감지 시, 모든 필드에 대한 업데이트가 발생하기 때문에 필드가 의도치않게 null
에 노출될 수 있는 위험이 있다. 또한, 실제 데이터 수정이 발생하는 영역과 변경 사항이 적용되는 영역이 분리되어 있어, 변경 사항을 추적하기 어렵다. 그러므로, merge()
사용을 지양하자.
다음은 준영속 상태의 Entity
를 변경감지를 통해 업데이트 하는 코드이다.
@Service
@AllArgsConstructor
public class ItemService {
ItemRepository itemRepository;
@Transactional
public Item updateItem(Long itemId, Item param) {
// findResult 객체는 영속 상태에 있으므로 변경감지가 동작한다
Item findResult = itemRepository.findOne(itemId).get();
// param은 준영속 상태의 Entity 객체이다
findResult.setName(param.getName())
findResult.setPrice(param.getPrice())
// param은 여전히 준영속 상태이다.
// 하지만, 반환되는 findResult는 영속 상태의 엔티티이다.
return findResult
}
}
엔티티에 setter 가 없는 경우는 어떻게 하나요?