@DynamicInert, @DynamicUpdate

Minjae An·2023년 12월 13일
0

Spring data JPA

목록 보기
1/5

🤖 JPA를 이용한 엔티티 등록 및 수정

JPA는 영속성 컨텍스트 라는 논리적인 공간을 이용하여 효과적으로 DB에 접근하고 데이터를
활용한다. 이때 엔티티를 DB에 저장하거나 DB에 존재하는 엔티티를 수정할 때 JPA의 기본전략은
엔티티의 모든 필드를 업데이트 하는 방식으로 이뤄진다.

즉, 특정 필드 값이 정의되거나 수정되지 않아도 JPA가 쿼리를 날릴 시 null 값으로 보내개 된다.

// TestEntity 클래스
@Slf4j
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table
@Entity
public class TestEntity extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    @Column
    private String testName;

    @Column
    private String testValue;

    @Column
    private String testMom;

    @Column
    private String testDad;

    @Column
    private String testBrother;
}
// TestRepository 클래스
@Repository
public interface TestRepository extends JpaRepository<TestEntity, Long> {

}

위 바탕에서 testNametestValue 값만 설정 후 엔티티를 등록하고 쿼리를 확인해보면

Hibernate: 
    insert 
    into
        test_entity
        (id, created_at, updated_at, test_brother, test_dad, test_mom, test_name, test_value) 
    values
        (default, ?, ?, ?, ?, ?, ?, ?) // 모든 칼럼에 대한 쿼리 생성

의도한 testNametestValue 칼럼을 제외하고 다른 칼럼들에 대한 쿼리 또한 만들어진 것을
확인할 수 있다.

이번에는 이미 저장된 엔티티의 testNametestValue 를 수정하는 로직을 바탕으로 생성된
쿼리를 관찰해보자.

Hibernate:  // <- 엔티티 조회하는 쿼리
    select
        testentity0_.id as id1_0_,
        testentity0_.created_at as created_2_0_,
        testentity0_.updated_at as updated_3_0_,
        testentity0_.test_brother as test_bro4_0_,
        testentity0_.test_dad as test_dad5_0_,
        testentity0_.test_mom as test_mom6_0_,
        testentity0_.test_name as test_nam7_0_,
        testentity0_.test_value as test_val8_0_ 
    from
        test_entity testentity0_ 
    where
        testentity0_.test_name=?
Hibernate:  // <- 엔티티 수정하는 쿼리
    update
        test_entity 
    set
        created_at=?,
        updated_at=?,
        test_brother=?,
        test_dad=?,
        test_mom=?,
        test_name=?,
        test_value=? 
    where
        id=?

업데이트를 의도했던 칼럼외 다른 칼럼을 포함한 수정 쿼리가 발생한 것을 확인할 수 있다.

왜 JPA 구현체는 불필요하게 모든 필드를 업데이트할까?

모든 필드를 업데이트하면 DB에 전송되는 데이터량이 증가한다는 단점이 있지만, 아래 두 장점을
얻을 수 있다.

  • 모든 필드를 사용하면 바인딩 되는 데이터만 다를 뿐 등록 수정, 쿼리가 항상 같다. 따라서
    애플리케이션 로드 시점에 쿼리를 미리 생성해두고 재사용할 수 있다.
  • DB에 동일한 쿼리를 보내면 DB는 이전에 한 번 파싱된 쿼리를 재사용할 수 있다.

즉 이점이 단점보다 크기에 JPA는 기본 전략으로 모든 필드 업데이트 방식을 사용한다. 하지만 어떤 경우에는 모든 필드를 업데이트 하는 것이 원치 않는 결과로 이어질 수 있다.

이를 위해 @DynamicInsert , @DynamicUpdate 가 존재한다.

🧐 @DynamicInsert, @DynamicUpdate에 대한 이해

이 애노테이션들을 사용하면 실제 등록, 수정되는 칼럼에 대한 쿼리가 발생된다.

앞선 상황에서 @DynamicInsert 를 적용할 경우

Hibernate: 
    insert 
    into
        test_entity
        (created_at, updated_at, test_name, test_value, id) 
    values
        (?, ?, ?, ?, default)

실제 값이 지정된 필드인 testNametestValue 필드에 대한 쿼리만 날라간 것을 확인할 수 있다.

@DynamicUpdate 를 적용할 경우

Hibernate:  // <- 엔티티 조회하는 쿼리
    select
        testentity0_.id as id1_0_,
        testentity0_.created_at as created_2_0_,
        testentity0_.updated_at as updated_3_0_,
        testentity0_.test_brother as test_bro4_0_,
        testentity0_.test_dad as test_dad5_0_,
        testentity0_.test_mom as test_mom6_0_,
        testentity0_.test_name as test_nam7_0_,
        testentity0_.test_value as test_val8_0_ 
    from
        test_entity testentity0_ 
    where
        testentity0_.test_name=?
Hibernate: // <- 엔티티 수정하는 쿼리
    update
        test_entity 
    set
        updated_at=?, // 엔티티 리스너로 인해 만들어짐
        test_name=?,
        test_value=? 
    where
        id=?

의도한 대로 실제 수정되길 원했던 testNametestValue 에 대한 수정 쿼리가 만들어진 것을
확인할 수 있다.

👋 @ColumnDefault의 연관성

@ColumnDefault 는 해당 필드에 값에 대한 default 값을 설정할 수 있는 어노테이션이다. 즉 해당
어노테이션이 선언되어 있는 칼럼의 값을 채우지 않으면 지정한 default 값으로 채워진다.

하지만, 별도의 설정 없이 이 애노테이션만을 사용하면 의도한대로 동작하지 않는다.

해당 어노테이션은 필드 값이 아무 값도 지정되지 않았을 때 적용되는데, JPA 기본 전략 사용시 모든 필드에 대한 쿼리가 적용됨으로 null 값이 채워진다. 이로 인해 @ColumnDefault 가 올바르게
적용되지 않는다. 이때 @DynamicInsert를 동반해야 의도했던 결과를 얻을 수 있다.

참고

profile
도전을 성과로

0개의 댓글