티스토리 뷰

카테고리 없음

jpa insert 시 default 값 적용

도토리_dev 2018. 11. 2. 18:58

spring-data-jpa 를 사용하여 프로젝트를 진행중에 insert (save()) 를 할경우 해당 테이블 컬럼의 default 값이 적용되지 않는 문제가 발생하였다.. 

막연하게 @Entity 객체의 null 인 필드는 insert 시 제외되는줄 알았는데 그게 아니였다..

해당 소스는 GitHub 에 있다.

사실 해당필드를 원시타입으로 지정할 수도 있고 private Integer likeCount = 0; 이런식으로 초기화가 가능하지만 이렇게 진행은 하지 않았다. 왜냐하면 업데이트될 entity 를 새로 생성하였을 경우 값이 항상 default 값을 가지게 되기 떄문에.. 다른 문제가 발생하였기 때문이다.

매체 테이블이 있다고 가정하고. (like_count 는 default 값으로 0 을 주었다.)

@Entity
@Getter
@Table(name = "MEDIA")
@NoArgsConstructor
public class Media {

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "idx")
private Long idx;

@Column(name = "name")
private String name;

@Column(name = "url")
private String url;

@Column(name = "like_count")
@ColumnDefault("0") //default 0
private Integer likeCount;

@Builder
public Media(String name, String url, Integer likeCount) {
this.name = name;
this.url = url;
this.likeCount = likeCount;
}
}

위 entity 를 create 하게 되면 아래와 같은 ddl 이 나온다.

create table media (

       idx bigint generated by default as identity,

        like_count integer default 0,

        name varchar(255),

        url varchar(255),

        primary key (idx)

    )


이제 Media 의 likeCount 값을 주지 않고 (null 이된다.) 저장을 해본다..

@Test
public void insert_test() {
Media media = Media.builder()
.name("mediaName")
.url("http://zum.com")

.build();

Media resultMedia = mediaRepository.save(media);

assertThat(resultMedia.getLikeCount(), Is.is(0)); //실패...
}

likeCount 필드값을 지정해주지 않았기 때문에 null 이 될테고. 그럼 default value 인 0 이 들어가는줄 알았으나.. 결과는 fail


실제 save 시 수행되는 쿼리는

Hibernate: 

    insert 

    into

        media

        (idx, like_count, name, url) 

    values

        (null, ?, ?, ?)


가 되며 like_count 값의 value에 null 이들어가게 된다... 두둥


  • 해결방법 1. @DynamicInsert, @DynamicUpdate 

null 인 필드값이 insert 나 update 시 제외되게 하는 방법은 org.hibernate.annotations. 패키지의

@DynamicInsert   (insert 시 null 인필드 제외)

@DynamicUpdate (update 시 null 인필드 제외)

를 사용한다.

@Entity
@Getter
@Table(name = "MEDIA")
@DynamicInsert
@NoArgsConstructor
public class Media {
...
}

@DynamicInsert 어노테이션을 사용하여 save 진행시 수행되는 쿼리는

Hibernate: 

    insert 

    into

        media

        (name, url, idx) 

    values

        (?, ?, null)

like_count 값이 null 이기 때문에 제외된것이 확인되었다~!

@Test
public void insert_test() {
Media media = Media.builder()
.name("mediaName")
.url("http://zum.com")

.build();

//사실 resultMedia likeCount 0이 되는줄 알았는데.. null 이다.
Media resultMedia = mediaRepository.save(media);

//한번더 조회를 해야 (selectMedia) likeCount 값이 0 entity 를 얻게된다.
Media selectMedia = mediaRepository.findById(resultMedia.getIdx())
.orElseThrow(NullPointerException::new);

assertThat(selectMedia.getLikeCount(), Is.is(0));    //성공!
}

  • 해결방법 2. @PrePersist, @PreUpdate

persist 되기 직전 호출되는 어노테이션 @PrePersist 를 사용하는 방법이 있다.


@Entity
@Getter
@Table(name = "MEDIA")
@NoArgsConstructor
public class Media {

...

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

해당 entity 에 @PrePersist 어노테이션을 추가하고 수행하여야 할 로직을 작성한다.

여기에선 this.likeCount 가 null 일경우 0 으로 default 값을 주었다.

@Test
public void insert_test() {
Media media = Media.builder()
.name("mediaName")
.url("http://zum.com")

.build();

//insert 되기 전(persist 되기전) likeCount 값이 null 이면 0 되기 떄문에 resultMedia likeCount 값도 0이된다.

Media resultMedia = mediaRepository.save(media);

assertThat(resultMedia.getLikeCount(), Is.is(0));    //성공!!
}


  • 결론

첫번째 방법 (@DynamicInsert) 과 두번쨰 방법 (@PrePersist) 을 고민하다 두번째 방법으로 진행하였다. 왜냐하면 코드로 로직을 조금이나마 더 파악할 수 있을것 같아서.. 이다.

insert 시 default 값 적용 을 파악하면서 여태 몰랐던 
@PrePersist
@PostPersist
@PreRemove
@PostRemove
@PreUpdate
@PostUpdate
@PostLoad
에 대한 기능을 알게 되어서 기쁘다..!
jpa 를 통해 실제 쿼리를 작성하지 않고 편하게 개발을 진행 할 수 있지만 그에따라 더 깊게 이해를 하고 잘.. 써야 할것같다.




댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함