Spring

[Spring] Spring Data JPA의 save 함수 구조

퉁그리 2022. 6. 17. 21:09

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 쿼리를 안나갈 수 있도록 할 수 있다.