본문 바로가기
★ 프로젝트 + 트러블 슈팅 ★

[DB ORM] INSERT DEFAULT 트러블 슈팅

by 리승우 2024. 7. 7.

이슈 내용

- flyway를 통한 DB 마이그레이션 툴을 이용하던 중, 특정 기능 구현으로 인해 post 테이블에 view_count와 likes_count를 추가해야하는 상황이 발생하였다. 

public class PostEntity extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "post_id")
    private Long id;

    private String userId;

    private String title;

    private String content;

    private String image;

    private String category;
    
    ** 아래 2개 컬럼 추가
    private Long view_count;

    private Long likes_count;

 

하여 flyway를 통한 DB 상태 동기화를 위해 flyway전용 파일을 생성한 후 애플리케이션 재실행을 통해 아래와 같이 DB 테이블을 업데이트하였다.

 

- V3__add_viewCountAndLikesCount.sql

ALTER TABLE post
    ADD COLUMN view_count BIGINT DEFAULT 0,
    ADD COLUMN likes_count BIGINT DEFAULT 0;

 

해당 작업을 실행한 후 테이블의 DDL을 확인하면 아래와 같이 나온다.

 

보시다시피 두개 컬럼이 추가되었다.

CREATE TABLE `post` (

`created_date_time` datetime(6) DEFAULT NULL,

`modified_date_time` datetime(6) DEFAULT NULL,

`post_id` bigint NOT NULL AUTO_INCREMENT,

`content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,

`image` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,

`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,

`user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,

`category` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,

`view_count` bigint DEFAULT '0',

`likes_count` bigint DEFAULT '0',

PRIMARY KEY (`post_id`)

) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

 

이제부터 이슈 내용이다.

현재 post 등록 버튼을 누르면 post 테이블에 row가 추가되는데, 

기본값으로 0을 지정하였으나 NULL이 배정되고 있다.

 

해결 과정

이유를 찾고자 등록 버튼을 누를 시 실행되는 SQL을 확인해보았다.

Hibernate: 
    insert 
    into
        post
        (category, content, created_date_time, image, likes_count, modified_date_time, title, user_id, view_count) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?)

 

현재 실제로 넘기는 데이터는 해당 2개 컬럼을 제외하고 넘기는데, insert 구문에서는 2개 컬럼도 추가되어 보내지고 있다.

아... NULL로 보내기 때문에 NULL로 받나보다.

 

DB단에서 DEFAULT 로 명시된 건 아래와 같은 의미이다.

  • DEFAULT : 특정한 값을 입력하지 않으면 default 값 지정, 'NULL' 은 하나의 특정한 입력이라고 판단

NULL을 던지니 NULL이 박힌 게 맞네~~~

 

해당 문제 해결을 위해 알아보던 중,

@DynamicInsert를 찾았다.

@DynamicInsert INSERT 구문 생성 시점에 null이 아닌 컬럼들만 포함하는 기능이다

Hibernate로 Spring Data JPA를 사용할 때 지원하는 기능이다.

 

Entity에 해당 어노테이션을 추가하고 다시 INSERT 구문을 실행하면 아래와 같이 NULL로 배정된 2개 컬럼을 제외하고 SQL이 실행된다.

이로써 기본값인 0이 할당되었다!

Hibernate: 
    insert 
    into
        post
        (category, content, created_date_time, modified_date_time, title, user_id) 
    values
        (?, ?, ?, ?, ?, ?)

 

 

해당 방법을 쓰면 INSERT구문이 실행될 때 불필요한 컬럼들도 제외해서 보내므로 미약한 쿼리성능 개선이 될 것 같다.

다른 방법이 또 없으려나 찾다가 아래 기능들을 발견했다.

 

Entity Listner

JPA의 리스너 기능은 엔티티의 생명주기에 따른 이벤트를 처리할 수 있게 한다.

  • PrePersist: 엔티티를 영속성 컨텍트스에 관리하기 직전
  • PostPersist: 엔티티를 데이터베이스에 저장한 직후
  • PreUpdate: 엔티티를 데이터베이스에 수정하기 직전
  • PostUpdate: 엔티티를 데티터베이스에 수정한 직후
  • PreRemove: 엔티티를 영속성 컨텍스트에서 삭제하기 직전
  • PostRemove: 엔티티를 데이터베이스에 삭제한 직후
  • PostLoad: 엔티티가 영속성 선텍스트에 조회된 직후 또는 refresh를 호출한 후

 

PrePersist를 쓰면 이것 또한 이슈 해결에 도움이 될 것으로 보인다.

Entity단에 아래와 같이 쓰면 될 것 같다.

    /**
     * insert 되기전 (persist 되기전) 실행된다.
     * */
    @PrePersist
    public void prePersist() {
    	this.view_count = this.view_count == null ? 0 : this.view_count
        this.likes_count = this.likes_count == null ? 0 : this.likes_count;
    }

 

 

하지만 나는 필요한 컬러만 INSERT날리는 @DynamicUpdate가 좋은 것 같아 1안으로 진행하기로 하였다.

유사 상황이 발생할 때마다 Entity단에 기능을 넣게되는 걸 허용하게 되면 추후 코드가 길어져 가독성이 떨어질 것이 우려되어 2안은 채택하지 않았다.

 

아무튼 이슈 해결!

 

 

참조

https://velog.io/@codren/DB-not-null-%EA%B3%BC-default-%EB%91%98%EB%8B%A4-%EC%8D%A8%EC%95%BC%EB%90%98%EB%8A%94%EA%B0%80

 

https://gaemi606.tistory.com/entry/JPA-DynamicInsert-DynamicUpdate-%EC%97%94%ED%8B%B0%ED%8B%B0-%EB%A6%AC%EC%8A%A4%EB%84%88

 

 

 

댓글