Spring Data JPA의 save() 메서드는 Entity가 새로운 객체인지 기존 객체인지 판단 후 INSERT 또는 UPDATE를 결정

 

🤔 새로운 Entity인지 판단하는 이유?

SimpleJpaRepository의 save() 메서드에서, isNew()를 사용하여 persist를 수행할지 merge를 수행할지 결정함

만약 ID를 직접 지정해주는 경우 신규 엔티티라고 판단하지 않아 merge를 수행함 - 비효율적

  • persist(): DB조회 없이 바로 INSERT
  • merge(): SELECT 조회 후 INSERT/UPDATE - 느림

 

 

JpaEntityInformation의 isNew(T entity)에 의해 판단

기본적으로 JpaMetamodelEntityInformation 클래스가 이 역할을 담당

// Spring Data JPA 내부에서 사용되는 인터페이스
public interface JpaEntityInformation<T, ID> {
	boolean isNew(T entity);
}
// JpaMetamodelEntityInformation
@Override
public boolean isNew(T entity) {
    
    // @Version 필드 X, @Version 필드가 primitive 타입인 경우
    if(versionAttribute.isEmpty() || 
       versionAttribute.map(Attribute::getJavaType).map(Class::isPrimitive).orElse(false)) {
       
         // AbstractEntityInformation의 isNew() 사용
         return super.isNew(entity);
	}
    
    // @Version 필드가 wrapper 클래스인 경우 - BeanWrapper를 사용해 entity 필드값에 접근
    BeanWrapper wrapper = new DirectFieldAccessFallbackBeanWrapper(entity);
    
    // @Version 필드 값이 null 인지 확인
    return versionAttribute.map(it -> wrapper.getPropertyValue(it.getName()) == null).orElse(true);
}

 

// AbstractEntityInformation
public boolean isNew(T entity) {
    ID id = getId(entity);
    Class<ID> idType = getIdType();
    
    if(!idType.isPrimitive()) {
        return id == null; // wrapper 타입이면 null 체크
    }
    
    if(id instanceOf Number) {
        return ((Number) id).longValue() == 0L; // primitive이면 0 체크
    }
    
    throw new IllegalArgumentException(String.format("Unsupported primitive id type %s", idType));
}

 

Case 1: @Version 필드가 wrapper 클래스인 경우

JpaMetamodelEntityInformation의 isNew(T entity) 사용

@Entity
public class User {
    @Id
    private Long id;
    
    @Version
    private Integer version;
}

version 필드가 null 이면 새로운 Entity

 

 

Case 2: 나머지 (ID 기반 판단)

- @Version이 없는 경우

- @Version이 primitive 타입인 경우

AbstractEntityInformation의 isNew(T entity) 사용

@Entity
public class User {
    @Id
    @GeneratedValue
    private Long id;
}
  • ID가 wrapper 클래스 → id가 null이면 새로운 Enitty
  • ID가 primitive 타입 id가 0이면 새로운 Entity

 

🤔 키 생성 전략을 사용하지 않고 직접 ID를 할당한 경우?

새로운 Entity로 간주하지 않음. 이때는 엔티티에서 Persistable<T> 인터페이스를 구현해서 JpaPersistableEntityInformation의 isNew()가 동작하도록 해야 함

+ Recent posts