회원 정보를 수정하는 경우 해당 회원은 새로 가입하는 것이 아닌 기존 정보를 수정하는 것이기 때문에 DB에 한번 저장되어서 식별자가 존재하는 상태이다. 이런 경우도 준영속 엔티티라고 하는데 준영속 엔티티는 영속성 컨텍스트가 더는 관리하지 않는 엔티티를 말한다.
다음과 같이 회원 수정 폼으로 변경 데이터를 받아와서 임의의 엔티티 객체를 생성하고 값을 전달하면 해당 엔티티는 준영속 상태이기 때문에 변경 감지가 이루어지지 않는다.
@Transactional
void update(UpdateForm updateForm) {
Member member = new Member();
member.setId(updateForm.getId());
member.setPhoneNumber(updateForm.getPhoneNumber());
}
그래서 준영속 상태의 엔티티를 영속 상태로 변경하는 방법으로 병합(merge) 기능이 있는데 준영속 엔티티를 파라미터로 전달하면 식별자 값으로 1차 캐시에서 엔티티를 조회하고, 1차 캐시에 엔티티가 없을 경우 DB에서 엔티티를 조회하여 1차 캐시에 저장한다. 그리고 조회한 영속 엔티티에 파라미터 준영속 엔티티의 값을 채워 넣은 뒤에 영속 상태의 엔티티를 반환한다. 이렇게 되면 트랜잭션 커밋 시점에 변경 감지 기능이 동작해서 DB에 UPDATE SQL이 실행되어 실제 변경이 이루어진다.
@Transactional
void update(UpdateForm updateForm) {
Member member = new Member();
member.setId(updateForm.getId());
member.setPhoneNumber(updateForm.getPhoneNumber());
Member mergeMember = em.merge(member);
}
하지만 병합 기능은 모든 필드를 교체해버리기 때문에 파라미터로 전달된 준영속 엔티티의 특정 필드가 비어있는 경우 null로 변경이 되는 위험이 있다.
수정 시 모든 값을 변경해버리는 상황은 거의 없기 때문에 엔티티를 변경할 때는 병합은 되도록 하지 않고 다음과 같이 식별자를 통해 엔티티를 조회해와서 변경 감지를 사용하는 것이 좋다.
@Transactional
void update(Member memberForm) {
Member findMember = em.find(Member.class, memberForm.getId());
findMember.setPhoneNumber(memberForm.getPhoneNumber());
}
'JPA' 카테고리의 다른 글
[JPA] 위치 좌표(위도, 경도) 다루는 법 (hibernate spatial ) (0) | 2023.01.15 |
---|---|
[JPA] fetch join 주의점 (0) | 2022.12.20 |
[JPA] 엔티티(Entity)에 기본 생성자를 선언해야 하는 이유 (Builder 패턴, static factory method) (0) | 2022.07.07 |
[JPA] JPQL (0) | 2022.07.05 |
[JPA] 프록시를 이용한 지연 로딩(FetchType.LAZY)과 연관관계 옵션(영속성 전이, 고아 객체) (0) | 2022.06.30 |