[Spring] Spring Data JPA의 save 함수 구조
Spring Data JPA의 save함수의 구조는 다음과 같다.
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) { // 1
em.persist(entity); // 2
return entity; // 3
} else { // 4
return em.merge(entity); // 5
}
}
1번의 isNew 함수를 통해 매개변수로 들어온 entity가 새로운 entity인지, 이미 저장된 entity인지 체크한다.
- 레퍼런스 타입(String, Long...)일 경우 null값이면 새로운 entity로 판단한다.
- 기본 타입(Primitive Type)[int, long, char etc..]일 경우 값이 0이면 새로운 entity로 판단한다.
따라서 id 생성 방식이 @GeneratedValue이라면 엔티티가 저장되기 전에는 id값이 null이거나 0이기 때문에 2번, 3번을 차례로 진행한다.
2번과정을 진행한 entity의 id값에는 @GeneragedValue 속성에 맞게 값이 채워져 있다.
그렇다면 id값이 @GeneratedValue방식이 아니라 사용자가 직접 입력하는 값이라면 어떤식으로 판별할까
우선 아무런 설정이 되있지 않다면, id값에는 보통 값이 채워져 있을 것이기 때문에, 1번구간을 넘어가지 못하고 4번으로 넘어가 merge를 하게 된다.
merge을 하게 될 경우 해당 entity가 데이터베이스에 존재하는지 체크하기 위해 select 쿼리가 한번 발생하게 된다.
insert가 발생하기 전 select가 한번 발생하는 것을 볼 수 있다.
이렇게 @GeneratedValue을 쓰지 않은 채로 별다른 설정이 없으면 매 저장시마다 Select 쿼리가 한번씩 발생할 수 밖에 없다.
이를 위해 다음과 같이 문제를 해결할 수 있다.
@Entity
@Getter
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Movie implements Persistable<Long> {
@Id
@Column(name="movie_id")
private Long id;
private String title;
private String thumbnail;
private String directorName;
private Integer year;
private String naverUrl;
private String nation;
private Double popularity;
private String actors;
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
private LocalDateTime lastModifiedDate;
@Override
public boolean isNew() {
return getCreatedDate() == null;
}
}
해당 entity에 Persistable<id타입>을 상속받아 isNew을 커스텀할 수 있는 방법이 있다.
이를 이용해 createDate이나 다른 기타 판별방식을 활용해 select 쿼리를 안나갈 수 있도록 할 수 있다.