[ JPA ] save() 전에, UPDATE 전에 왜 SELECT 가 발생할까? + INSERT 전에 SELECT 가 발생하는 이유는 무엇일까?

2023. 9. 22. 20:34·개인공부
목차
  1. 문제의 원인
  2. 또 다른 의문점, INSERT 전 SELECT가 발생하는게 맞는가?
  3. 해결책: Persistable 인터페이스 구현

개인 프로젝트에서 사용한 product 의 저장 및 업데이트 로직, productRepository 에서는 JpaRepository의 save 메서드를 사용한다.

 

나는 데이터를 저장 및 업데이트할 때, 각각의 메서드로 분리하여, JPA save() 메서드를 사용하였다. 그러다 어느날 다음과 같은 코드로직을 보게되었다.

 

1. findById 를 통해 AEntity를 조회, a변수 할당
2. 새로운 b변수에 a변수의 정보를 참조하여, new AEntity를 생성
3. b변수save()

 

나는 이 코드로직을 보고선, "AEntity 를 새로 생성하는구나" 라는 생각을 했다. 하지만 Hibernate 로그를 보았을 때는 "Insert가 아닌 Update 쿼리가 발생"하였다.

 

나는 의문이었다. "새로운 b변수에 new AEntity 를 생성했는데 어떻게 PK 중복 에러 없이, 업데이트가 발생했는가?" 궁금했다.

 

save() 를 두번 호출할 경우, Select가 먼저 발생하고선, Insert 가 아닌 Update 가 발생했다.

 


문제의 원인

 

이 답은, 김영한님의 자바 ORM 표준 JPA 프로그래밍의 내용을 통해 알 수 있었다.

 

save(S) 메소드는 엔티티에 식별자 값이 없으면(null이면) 새로운 엔티티로 판단해서 EntityManager.persist()를 호출하고 식별자 값이 있으면 이미 있는 엔티티로 판단해서 EntityManager.merge()를 호출한다.

 

이와 관련하여 SimpleJpaRepository(CrudRepository의 구현체)를 살펴보면 save() 는 아래와 같이 구현되어있다.

 

SimpleJpaRepository 내 save 메서드의 구현된 코드의 모습

 

entityInformation.isNew 를 타고 들어갔을 때의 최종 종착지

  • isNew(): idType이 Primitive 타입이 아닌지 확인을 한다. (JpaRepository 상속 시 입력한 ID 타입을 보고 판단) 그리고 그게 null인지 아닌지 판단한다.
  • persist(): 해당 객체를 영속성으로 만든다.
  • merge(): 해당 객체를 조회한 후 있으면, UPDATE, 없다면 INSERT 를 통해 새로운 영속성 객체를 반환한다.

 

즉, findById() 를 통한 AEntity 조회된 AEntity를 기반으로 새로운 AEntity 를 생성(식별자 존재)하였고 save() 할 경우, 식별자가 존재하여 Insert 가 아닌 Update 가 발생 했던 것이다.

 

하지만 또 다른 의문점이 생겼다. "저장되지 않은 Entity 를 save(Insert) 할 경우에는 어떻게 되는거지? 나는 식별자를 생성하고서 DB에 save() 를 진행하는데?"


또 다른 의문점, INSERT 전 SELECT가 발생하는게 맞는가?

 

나는 productId 를 식별자로 사용하고 있으면서, 객체를 생성할 때, 도메인 로직을 거쳐, 해당 Product 를 생성한다.

  • 도메인 로직 간, initializeProduct 를 통해, 식별자(@Id 에 해당하는 값)를 생성 및 할당한다.
  • 현재 Product 및 다른 모든 도메인 엔터티에 대하여, INSERT 전 SELECT 가 발생하는 구조의 설계를 갖고 있다.

 

Product 클래스 내에 선언된 initializeProduct 메서드

 

실제로 테스트 해본결과, 로그는 예상대로 SELECT 가 발생하는 것을 확인할 수 있었다.  그리고 나는 생각하게되었다. "INSERT/UPDATE 전에 SELECT를 하는 것이 맞을까?"

 

이에대한 내 생각은 "UPDATE 를 위한 SELECT 는 맞지만, INSERT에서는 아니다." 라고 생각한다. 그렇다면 이를 어떻게 해결할 수 있을까?

 

신규 ProductEntity 를 save 할 경우, INSERT 말고도 SELECT 가 먼저 발생한다.

 


해결책: Persistable 인터페이스 구현

 

entityInformation.isNew 를 타고 들어갔을 때의 최종 종착지

 

기존의 isNew의 경우, Id 값의 존재 여부로 결정되므로, 이를 다른 조건으로 개선하면된다.

  • 방법 1. 생성시간에 대한 필드를 추가하고, 해당 필드 값의 null 여부에 따라 isNew 를 판별한다.
  • 방법 2. isNew 필드를 추가하고, 해당 값을 변환하는 작업을 하는 메서드를 추가한다.

 

// 방법 1. 생성시간에 대한 필드를 추가하고, 해당 필드 값의 null 여부에 따라 isNew 를 판별한다.
@CreatedDate
private LocalDateTime createdDate;

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

 

// 방법 2. isNew 필드를 추가하고, 해당 값을 변환하는 작업을 하는 메서드를 추가한다.
@Transient
private Boolean isNew = true;

@PrePersist // 영속성 객체로 만들어질 때 false로 변경
@PostLoad // DB에 저장하거나 불러온 시점 이후부터는 새로운 entity로 인식하지 않게 처리
private void markNotNew() {
  isNew = false
}

 

아직 어느것이 더 나은 방법인지는 판단을 내리지 못했지만, 분명한것은 Persistable 인터페이스를 구현하여 기존의 INSERT API 실행 시 SELECT가 발생하지 않도록 개선해야겠는 생각이다. 우연한 기회로 이 부분을 알게되고 또 공부할 수 있어서 다행이다!


 

[JPA] save메서드로 살펴보는 persist와 merge 개념

JpaRepository의 구현체 SimpleJpaRepository에서 save()메서드가 내부적으로 어떻게 동작하는지 알아보자.

umanking.github.io

 

 

Spring Data JPA에서 Insert 전에 Select 하는 이유

이전 글에서 bulk insert 처리를 할 수 있는 방법 중에 하나가 @Id 값 알고 있는 경우라고 했었는데요. 실제로 잘 되는지 확인하는 과정에 발생한 문제 내용을 공유합니다.

kapentaz.github.io

 

 

JPA에서 insert, update, delete 할 때 자동으로 select 하지 않게 하는 방법

JPA에서 CRUD 중 Create(insert), Update, Delete query를 할 때, 원하지 않는 select query가 발생합니다. 다음 예제를 먼저 보겠습니다. select query가 발생하는 예제 먼저 User entity class를 생성합니다. import lombok.Ge

yjh5369.tistory.com

 

728x90
저작자표시 (새창열림)

'개인공부' 카테고리의 다른 글

[ 코드 설계 ] 의존성 역전 원칙 과 의존성 주입 프레임 워크  (0) 2023.10.06
[ 코드 설계 ] 개방 폐쇄 원칙 OCP  (0) 2023.09.28
[ 코드 설계 ] 단일 책임 원칙 SRP  (0) 2023.09.26
[ Java ] Optional  (0) 2023.09.18
[ Spring ] @ControllerAdvice 를 통한 @Controller 전역 Exception 처리, Controller 를 보다 명확하게 표현하기  (0) 2023.08.11
  1. 문제의 원인
  2. 또 다른 의문점, INSERT 전 SELECT가 발생하는게 맞는가?
  3. 해결책: Persistable 인터페이스 구현
'개인공부' 카테고리의 다른 글
  • [ 코드 설계 ] 개방 폐쇄 원칙 OCP
  • [ 코드 설계 ] 단일 책임 원칙 SRP
  • [ Java ] Optional
  • [ Spring ] @ControllerAdvice 를 통한 @Controller 전역 Exception 처리, Controller 를 보다 명확하게 표현하기
KEEMSY
KEEMSY
JUST DO IT
KEEMSY
목적, 수단, 목표
KEEMSY
전체
오늘
어제
  • 분류 전체보기
    • 회고
      • WIL
      • TIL
    • Project
    • 개인공부
      • 알고리즘
      • 아키텍처
      • 트러블슈팅
      • 테스팅
      • git
      • 배포

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

hELLO· Designed By정상우.v4.5.2
KEEMSY
[ JPA ] save() 전에, UPDATE 전에 왜 SELECT 가 발생할까? + INSERT 전에 SELECT 가 발생하는 이유는 무엇일까?

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.