JPA

[Spring Data JPA] 새로운 엔티티 구별 방법

감자b 2024. 12. 30. 00:15

JPA에서 제공하는 save() 메서드는 아래와 같이 동작한다.

  • 새로운 엔티티면 저장(persist)
  • 새로운 엔티티가 아니면 병합(merge)

여기서 새로운 엔티티의 판단 기준은 다음과 같다.

  • 식별자가 객체이고 null일 경우
  • 식별자가 자바 기본타입이고 0일 경우
  • Persistable 인터페이스

만약 엔티티의 키 생성 전략이 자동 생성 전략이라면 save()가 호출될 때 식별자가 존재하지 않는다.

이는 새로운 엔티티로 인식해서 persist로 동작하므로 문제가 발생하지 않는다.

하지만 ID를 직접 할당하는 경우 이미 식별자가 존재하므로 새로운 엔티티가 아니라고 판단하고 merge를 하게 된다.

merge()는 주로 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용되는데, 동작 과정은 다음과 같다.

  1. merge(member) → member는 준영속 상태
  2. member의 식별자로 1차 캐시에서 엔티티 조회
    • 만약 1차 캐시에 없다면 데이터베이스에서 엔티티 조회
  3. 조회한 엔티티(영속)에 member 값을 채워서 병합 후 반환

여기서 만약 member의 필드가 nickname, name, age … 등 여러 개가 있을 때 값을 채워넣지 않고 merge를 할 경우 빈 값은 모두 null로 들어간다. (값을 완전히 바꿈)

즉 위험한 상황이 발생할 수 있으며, DB를 호출해서 값을 확인하므로 비효율적이다.

 

따라서 값을 직접 할당해서 저장해야 하는 경우 Persistable 인터페이스에서 새로운 엔티티 확인 여부를 직접 구현하여 merge()하는 것을 방지할 수 있다.

public interface Persistable<ID> {
     ID getId();
     boolean isNew();
}

 

 

Auditing의 등록 일자 필드를 사용하면 새로운 엔티티 여부를 편리하게 확인할 수 있다.

(@CreatedDate에 값이 없으면 새로운 엔티티)

@Entity
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item implements Persistable<String> {
	@Id
	private String id;
    
	@CreatedDate
	private LocalDateTime createdDate;
    
	public Item(String id) {
		this.id = id;
	}
     
	@Override
	public String getId() {
		return id; 
	}

	@Override
	public boolean isNew() {
		return createdDate == null;
	}
}

참고

 

실전! 스프링 데이터 JPA 강의 | 김영한 - 인프런

김영한 | 스프링 데이터 JPA는 기존의 한계를 넘어 마치 마법처럼 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 모두 제

www.inflearn.com