(JPA) NotNull vs Column nullable=false

JPA에서 null을 검증하는 방법 두가지를 비교해봅니다.

Validator 어노테이션이 DDL에도 영향을 주는것을 확인해봅니다.


Goal

  • @NotNull 어노테이션이 DDL에 영향을 주는것을 확인해봅니다.
  • @NotNull@Column(nullable = false) 동작방식의 차이를 확인해봅니다.

@NotNull과 @Column(nullable = false)

@NotNull 사용시에도 DDL에 NotNull 옵션이 들어가는것을 확인해봅니다.

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
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
//    @Column(nullable = false)
    private String name;

    public Member(final String name) {
        this.name = name;
    }
}
  • 기존까지는 Spring Validator인 @NotNull 사용시, 필드값 자체만 검증을 해준다고 생각했습니다.
  • @Column(nullable = false) 을 해야지만, DDL에 NOT NULL 옵션이 들어간다고 생각했었습니다.
  • 하지만 완전히 잘못생각하고 있었습니다.
1
2
3
4
5
6
7
8
// application.yml 
spring:
  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        format_sql: true

image

1
2
3
4
5
create table member (
    id bigint generated by default as identity,
    name varchar(255) not null,     -- not null 옵션이 들어가 있습니다.
    primary key (id)
)
  • DDL을 볼때 @NotNull 어노테이션을 사용하여도, name에 not null 옵션이 들어가는것을 확인할 수 있습니다.

Hibernate에서는 @NotNull 어노테이션을 지원한다.

Hibernate 객체 관계형 매퍼를 사용하는 경우 모델에 대한 DDL을 생성할 때 일부 제약 조건이 고려됩니다
(“Hibernate 메타데이터 영향” 참조).

  • 공식문서를 보면, @NotNull 어노테이션을 인식 후 DDL스키마 데이터로 변환하는것을 확인 할 수 있습니다.

image

properties

1
spring.jpa.properties.hibernate.validator.apply_to_ddl=false

yml

1
2
3
4
5
6
spring:
  jpa:
    properties:
      hibernate:
        validator:
          apply_to_ddl: false

image

  • 이부분에 대해 설정을 풀어주려면, 위와같이 false값을 명시적으로 넣어주어야 합니다. (기본속성은 true)

@NotBlank, @NotEmpty은 지원하지 않습니다.

not null 속성이 들어가지 않습니다.

image

  • @NotNull이 되니 당연히 되지 않을까라고 생각을 해보았는데 지원하지 않는것을 확인 할 수 있었습니다.
  • 과거 2.0 버전 미만 인경우에는 Hibernate Validator에서 구현한 @NotEmpty@NotBlank@NotNull을 코드에 포함하고 있어서 지원을 했습니다.
  • 하지만, 2.0 버전으로 넘어오면서 지원이 중단되게 됩니다.
  • 이부분에 대해서 관련된 이슈 링크

@Column(nullable = false) vs @NotNull

예외를 트리거하는 타이밍의 차이가 있습니다.

  • 가능하면 @NotNull을 쓰는것을 추천합니다.
  • nullable = false 옵션의 경우, DB에 Insert 쿼리를 쏘는 상황에서야 예외를 터트리게 됩니다.
  • 하지만 @NotNull의 경우 엔티티를 만드는것 자체는 가능하나, Repository에 잘못된 엔티티를 저장할때, ConstraintViolationException를 터뜨리므로 더 빠른 순간에 문제를 잡아낼 수 있습니다.

Exception thrown when an action would violate a constraint on repository structure. For example, when an attempt is made to persistently add an item to a node that would violate that node’s node type.

1
2
Member member = new Member(null); // 예외발생 x
Member savedMember = memberRepository.save(member); // 예외발생 o
1
2
3
4
5
6
7
8
// @NotNull
@Column(nullable = false)
private String name;

---

// Exception
org.springframework.dao.DataIntegrityViolationException: could not execute statement;
1
2
3
4
5
6
7
8
9
10
11
@NotNull
// @Column(nullable = false)
private String name;

---

// Exception
javax.validation.ConstraintViolationException: Validation failed for classes [com.example.playground.domain.Member] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
	ConstraintViolationImpl{interpolatedMessage='널이어서는 안됩니다', propertyPath=name, rootBeanClass=class com.example.playground.domain.Member, messageTemplate='{javax.validation.constraints.NotNull.message}'}
]

Conclusion

  • @NotNull 어노테이션으로도 DDL에 속성을 넣어줄 수 있다.
  • 하지만 @NotBlank, @NotEmpty의 경우에는 not null 속성이 들어가지 않는다.
  • @NotNull 어노테이션이 예외 검출 순간이 더 빨라 추천한다.

Github

  • 관련된 코드는 이쪽에서 확인해보실 수 있습니다.

Reference