@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
📌 새로운 엔티티를 판단하는 기본 전략
- 식별자가 객체인 경우, null로 판단
- 식별자가 자바 기본타입인 경우, 0으로 판단
- 필요시 Persistable 인터페이스를 구현해서 직접 판단 로직을 정의할 수도 있음
[참고] JPA 식별자 생성 전략이 @GeneratedValue 인 경우
- 기본적으로 flush하는 시점에 jpa가 식별자를 만들어줌
- 따라서 save() 호출 시점에는 식별자가 없으므로 새로운 엔티티로 인식하여 정상 동작
- 하지만 @Id만 사용하고 식별자를 직접 할당하는 경우에는 이미 식별자 값이 있는 상태로 save()를 호출 -> 당연히 jpa는 새로운 엔티티가 아니라고 판단하여 merge() 호출
- merge()의 경우, 기본적으로 해당 엔티티가 이미 db에 존재함을 가정하므로, 우선 select 쿼리를 날려서 값을 확인하고 db에 값이 없을 경우 그제서야 새로운 엔티티로 인지 -> 매우 비효율적 프로세스
- 이런 경우, Persistable 기능을 사용하여 새로운 엔티티 판단 로직을 직접 구현하는 것이 효율적
- 참고로 등록시간(@CreatedDate)을 조합하여 사용하면 편리하게 판단 가능
- 등록시간 값이 없는 경우 새로운 엔티티로 판단
package org.springframework.data.domain;
public interface Persistable<ID> {
ID getId();
boolean isNew();
}
@Entity
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item implements Persistable<String> {
// @Id @GeneratedValue
// private Long id;
@Id
private String id;
@CreatedDate
private LocalDateTime createdDate;
public Item(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
//isNew 판단을 위한 로직을 직접 작성
@Override
public boolean isNew() {
return createdDate == null;
}
}