ysk(0soo)
Lifealong
ysk(0soo)
전체 방문자
오늘
어제
  • 분류 전체보기 (238)
    • Java (50)
      • whiteship-java-study (11)
      • Java (28)
      • time (6)
    • Spring (68)
      • JPA (15)
      • Spring (1)
      • SpringBoot (1)
      • SpringMVC (6)
      • Spring Security (22)
      • Jdbc (1)
      • RestDocs (14)
      • log (6)
    • Kotlin (3)
    • Web (2)
      • nginx (1)
    • Database (14)
      • MySQL (5)
      • PostgreSQL (1)
      • SQL (1)
      • Redis (4)
    • C, C++ (0)
    • Git (1)
    • Docker (2)
    • Cloud (3)
      • AWS (3)
    • 도서, 강의 (0)
      • t5 (0)
    • 기타 (7)
      • 프로그래밍 (1)
    • 끄적끄적 (0)
    • CS (14)
      • 운영체제(OS) (2)
      • 자료구조(Data Structure) (9)
    • 하루한개 (12)
      • 우아한 테크코스-10분테코톡 (12)
    • 스터디 (12)
      • 클린 아키텍처- 로버트마틴 (2)
      • JPA 프로그래밍 스터디 (10)
    • 테스트 (34)
      • JUnit (19)
      • nGrinder (2)
      • JMeter (0)
    • Infra (3)
    • 프로그래머스 백엔드 데브코스 3기 (0)
    • 디자인 패턴 (3)
    • Issue (4)
    • system (1)
      • grafana (0)
      • Prometheus (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • github

공지사항

인기 글

태그

  • FilterSecurityInterceptor
  • querydsl
  • 정규표현식
  • AuthenticationException
  • 동일성
  • scope value
  • junit5
  • 가상 스레드
  • tree
  • 인가(Authorization) 처리
  • 동등성
  • java
  • DataJpaTest
  • 트랜잭션
  • AccessDecisionVoter 커스텀
  • UserDetailsService
  • StructuredConcorrency
  • LocalDateTime
  • VirtualThread Springboot
  • nginx basic auth
  • mysql
  • node exporter basic auth
  • 동시성 제어
  • restdocs enum
  • 가상 스레드 예외 핸들링
  • 구조화된 동시성
  • nGrinder
  • AccessDecisionManager
  • restdocs custom
  • jpa

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
ysk(0soo)

Lifealong

Spring/JPA

Jpa Insert, update delete 시 select query를 통한 먼저 조회하지 않는 방법.md

2023. 1. 18. 02:57

Spring Data Jpa에서는 대부분 JpaRepository interface를 상속받는데, 이 때 기본 구현체로 SimpleJpaRepository.class가 사용된다.

다음은 SimpleJpaRepository의 save method 코드이다.

@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);
        }
    }

}
  • JpaRepository.save 동작방식은 persist 하거나 merge를 한다.
  • 엔티티에 Id 값이 있으면 DB를 조회 (select)한 후에 insert 하게 된다.

Spring Data Jpa는 Id 생성전략이 존재하지 않을 경우 merge 로 동작하고 생성전 략이 존재하는 경우 persist 로 동작한다.

여기서의 핵심은 isNew() method인데 isNew() method에서 select query를 수행하여 새로운 데이터인지 확인한다.

select query 하지 않게하려면 Entity class에 Persistable 인터페이스를 구현하면된다.

package org.springframework.data.domain;

public interface Persistable<ID> {

    // 엔티티의 아이디를 반환하며, null 일 수 있.
    @Nullable
    ID getId();

    // Persistable이 new 이거나 이미 persistence 인 경우. 
    boolean isNew();
}

엔티티

@Entity
public class User implements Persistable<String> {

    @Id
    @GeneratedValue
    private String id;

    @Override
    public String getId() {
        return id;
    }

    @Override
    public boolean isNew() {
        return false;
    }
}

isNew() 메소드로 구분하는데 true이면 select 하지 않고 insert 한다. 그렇다고 무조건 true로 두게 되면 update할 때에도 insert를 수행하게 된다.

그러면 어떻게 isNew값을 제대로 뽑을 수 있을까?

일반적으로 @EnableJpaAuditing 의 createdDate를 이용하면 된다.

@CreatedAt을 사용하면 생성시에 createdAt이 무조건 생성되게 된다. 이 createdAt이 null인지 아닌지 비교하는 방법으로 알 수 있다.

@Entity
public class User implements Persistable<String> {

    @Id
    @GeneratedValue
    private String id;

    @CreatedDate
    private LocalDateTime createdAt;

    @Override
    public String getId() {
        return id;
    }

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

}
  1. @CreatedDate Annotation을 사용하면 data가 insert 될 때 자동으로 현재 시간이 입력된다.
  2. createdDate가 null이라면 새로운 데이터로 취급한다.

위처럼 변경하더라도 이미 존재하는 PK를 가진 데이터를 insert 시도하면 에러가 발생한다.

기존 동작인 select - insert를 유지하는 경우 엄청난 문제가 발생할 수 있기 때문에 로직을 잘 짜야 한다.

업데이트할 때도 업데이트해야 할 필드만 선택적으로 하는 것이 아닌 객체 전체가 변경되기 때문에 merge() 기능을 의도하고 사용해야 할 일은 거의 없다. 변경을 위해선 변경 감지(dirty-checking)를, 저장을 위해선 persist()만이 호출되도록 유도해야 실무에서 성능 이슈 등을 경험하지 않을 수 있다.

참조

  • https://yjh5369.tistory.com/entry/JPA%EC%97%90%EC%84%9C-insert-update-delete-%ED%95%A0-%EB%95%8C-%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C-select-%ED%95%98%EC%A7%80-%EC%95%8A%EA%B2%8C-%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95
저작자표시 비영리 (새창열림)

'Spring > JPA' 카테고리의 다른 글

QueryDSL 에러 - Execution failed for task ':compileQuerydsl', ClassNotFoundException: org.gradle.wrapper.GradleWrapperMain  (0) 2023.01.18
QueryDSL 설정 - SpringBoot 2.7.X & SpringBoot 3.x 버전  (0) 2023.01.18
Jpa Id 생성전략에 따른 동작 방식  (0) 2023.01.18
JPA Hibernate Id 생성 전략과 UUID 전략  (0) 2023.01.18
Jpa Hibernate Custom Id Generator  (1) 2023.01.18
    'Spring/JPA' 카테고리의 다른 글
    • QueryDSL 에러 - Execution failed for task ':compileQuerydsl', ClassNotFoundException: org.gradle.wrapper.GradleWrapperMain
    • QueryDSL 설정 - SpringBoot 2.7.X & SpringBoot 3.x 버전
    • Jpa Id 생성전략에 따른 동작 방식
    • JPA Hibernate Id 생성 전략과 UUID 전략
    ysk(0soo)
    ysk(0soo)
    백엔드 개발을 좋아합니다. java kotlin spring, infra 에 관심이 많습니다. email : kim206gh@naver.com github : https://github.com/devysk

    티스토리툴바