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

공지사항

인기 글

태그

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

최근 댓글

최근 글

티스토리

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

Lifealong

Spring/JPA

@NotNull vs @Column(nullable = false), JPA에서 INSERT 전에 Null 검사하는 방법

2022. 12. 9. 14:43
  • @Column(nullable=false) 옵션은 기본적으로는 INSERT 시 null값 삽입을 막아주지 못한다.

 

결론부터,

  • @Column(nullable = false) 만 필드에 사용하면, INSERT시에 Null 검사 를 하지 않는다
    • 이것은 기본 정책이며 바꿀 수 있다.
    • @Column(nullable = false) 만 사용해서 null값 방지를 하려면 spring.jpa.properties.hibernate.check_nullability=true 옵션 사용.
    • 아래를 참고하자.
  • Null검사를 하고 싶다면 @NotNull 어노테이션을 사용하여 @Column(nullable = false) 와 함께 정의하면, INSERT 전에 방지한다.

 

JPA @Column(nullable = false)

JPA 에서 Entity 저장시에 데이터베이스에 null 값을 저장하는 것을 방지하기 위해서는 Validation이 필요하다.

그럴 때, 보통 @Column 어노테이션을 이용하여 @Column(nullable = false) 을 이용한다.

엔티티로 삼을 객체에 @Entity 어노테이션을 붙이고,

추가적으로 여러 매핑 정보를 엔티티의 필드 위에 추가하여, 자동 생성되는 DDL에 제약조건을 추가할 수 있다.

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String isbn;
}
  • 다음과 같이 쿼리가 DDL SQL문이 실행되는것을 볼 수 있다.
create table book (
    id bigint generated by default as identity,
    isbn varchar(255) not null,
    primary key (id)
)

@Column(nullable = false)

  • nullable은 @Column의 속성 중 하나로, 기본값은 true이다.
  • 값을 false로 설정해 주면, 해당 필드는 DDL 생성 시 not null이라는 조건이 붙은 채로 생성된다.

하지만 데이터베이스 저장시에는 엔티티 필드 값이 Null이여도 유효성 검사를 해주지 않으며, 실제 DB로 쿼리가 날라간 후에 예외가 발생한다.

org.hibernate.PropertyValueException: not-null property references a null or transient value : jpabook.jpashop.domain.BookEntity

이 때, javax.validation.constraints의 @NotNull 어노테이션도 엔티티에 붙여 사용할 수 있다.

 

Java Bean Validation

  • 어노테이션 형태로 제약 조건을 달아줘서 쉽게 검증할 수 있도록 돕는 API이다.
  • Bean Validation은 인터페이스로 된 명세일 뿐이고 실제 동작할 수 있도록 구현한 것이 Hibernate Validator이다.
  • javax.validation 패키지를 사용하며 의존성을 추가해야 한다.
implementation 'org.springframework.boot:spring-boot-starter-validation'
  • 현재는 Jakarta Bean Validation 로 명칭이 바뀌였다.
    • https://beanvalidation.org/
    • https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/
  • 기본 스프링 패키지에 등록되어 있지 않은 이유
    • https://github.com/spring-projects/spring-boot/issues/19550

@NotNull @NotEmpty @NotBlank 등 다양한 어노테이션을 지원한다.

 

@NotNull과 @Column(nullable = false)의 차이

엔티티 필드의 null을 검증하기 위해서 대표적으로 사용되는 어노테이션이 @Column(nullable = false)이다.

  • @NotNull과는 차이가 있다.

언뜻 보기에 @NotNull\ 및 @Column(nullable = false)\ 주석이 모두 동일한 목적 을 수행하고 서로 교환하여 사용할 수 있는 것처럼 보이지만, 그렇지 않다.

@Column(nullable = false)을 사용할 때와 마찬가지로 @NotNull 어노테이션 적용 시, 테이블 생성시 NOT NULL DDL이 입력된다. 이는 Hibernate가 @NotNull 어노테이션을 해석할 수 있기 때문이다. 만일 Hibernate가 해석하길 원하지 않는 경우 application.properties에 아래 옵션을 지정해주면 된다.

# application.properties
spring.jpa.properties.hibernate.validator.apply_to_ddl=false
  • Hibernate 공식 문서

 

@Column(nullable=false) 시에 @NotNull 없이, INSERT 방지하는 방법

  • 이 옵션은 바꿀 수 있다
  • Hibernate 는 해당 필드가 @Column(nullable = false)\ 로만 주석이 달린 경우에도 가능한 null\ 값 에 대해 엔티티의 유효성 검사를 수행할 수 있다.
  • https://www.baeldung.com/hibernate-notnull-vs-nullable#1-validation
  • 이 Hibernate 기능을 활성화하려면 명시적으로 hibernate.check_nullability 속성을 true 로 설정해야 한다
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.check_nullability=true
  • 이 경우 Hibernate가 삽입 SQL 쿼리를 데이터베이스에 보내지 않는다.
  • default 설정은 다음과 같다. (기본 정책)
    • Bean Validation이 클래스패스에 있고, Hibernate Annotation이 사용되면 기본 값은 false - 검사를 안 한다.
    • Bean Validation이 클래스패스에 없고 Hibernate Annotation을 사용 안하면 true 이다. - 검사를 한다
  • 이것도 베스트 프랙티스가 아니라며 @PrePersist, @PreUpdate 이런 것을 쓰면 더 좋다고 한다
  • 아마, 인설트나 업데이트 호출 전에 널 검사를 하는것 같다.
  • https://jeong-pro.tistory.com/233

 

그렇다면 @NotEmpty와 @NotBlank는?

Spring Bean Validation에서는 @NotNull 외에도, 이와 비슷한 어노테이션을 제공한다.

  • @NotNull : null이 들어갈 경우 예외를 발생시킨다.
  • @NotEmpty : 문자열에 null이나 ""가 들어가면 예외를 발생시킨다. NotNull의 문자열 버전 상위호환.
  • @NotBlank : 문자열에 null, "", " "가 들어가면 예외를 발생시킨다. NotEmpty의 상위호환.

위의 @NotEmpty와 @NotBlank는, 문자열에만 적용할 수 있는 검증 어노테이션이다.

단순하게 생각했을 때에는, @NotEmpty와 @NotBlank 도 not null 옵션을 자동으로 붙여줄것 같지만 그렇지 않다.

  • Hibernate 공식 문서에 따르면 아쉽게도 Hibernate가 별도의 DDL을 지원해주지 않는다.

@NotBlank
Checks that the annotated … (생략)

Hibernate metadata impact
None


@NotEmpty
Checks whether the annotated element is not null nor empty

Hibernate metadata impact
None


@NotNull
Checks that the annotated value is not null

Hibernate metadata impact
Column(s) are not nullable

 

Hibernate 공식 문서

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}

위의 코드는 공식 페이지의 메인에서 보여주는 예시 코드이다.

다음과 같이 권하고 있다.

  • @NotBlank나 @NotEmpty를 통해 not null 옵션을 조절하는 대신,
  • @NotNull을 통해 DB에 not null 옵션을 주고 @Size를 통해 저장할 때 서버에서 추가로 검증을 하도록 한다.

일관성을 위해 @NotNull에 대해서만 not null 옵션으로 번역해 주는것 같다.

  • 빈 값은 null이 아니고, 빈 값은 저장될 수 있으니까?

 

엔티티에 검증 로직이 필요한 이유

단점은 명확하다. 어노테이션이 많아서 코드가 더러워 보인다.
하지만, 다음도 생각해 보아야 한다.

  • 의도치 않게 엔티티에 잘못된 값 (null 등)을 넣어도, Repository에 저장할 때 예외를 발생시켜 더 큰 문제를 막을 수 있다.
  • @NotNull 어노테이션을 통해 @Column의 nullable을 대체할 수 있다.
  • 다른 사람이 코드를 봤을 때, 어떤 제약 조건이 있는지 파악하기 쉽다.
  • 비즈니스 로직과 데이터 유효성 검사 로직 분리를 통해 애플리케이션을 깔끔하게 개발할 수 있게 해준다.

 

참조

  • https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#validator-integration
  • https://docs.jboss.org/hibernate/validator/6.0/api/index.html?org/hibernate/validator/constraints/NotEmpty.html
  • https://www.baeldung.com/hibernate-notnull-vs-nullable
  • https://meetup.toast.com/posts/223
  • https://kafcamus.tistory.com/15
저작자표시 비영리 (새창열림)

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

Jpa 쿼리 파라미터 로그 확인방법 - With DataJpaTest p6spy  (1) 2022.12.17
영속성 전이(Cascade) ManyToOne 시 주의할점 - 상위 엔티티 삭제 문제  (1) 2022.12.15
Spring Data Jpa Cursor based Pagenation (커서 기반 페이지네이션) 예제 (JpaRepository 커서 기반 페이지네이션  (0) 2022.12.14
JPA Entity의 Field Data Type은 primitive, Wrapper 중 어떤것을 사용해야 할까?  (0) 2022.12.10
JpaRepository에서 save시 select 쿼리가 먼저 실행되는 이유  (0) 2022.12.07
    'Spring/JPA' 카테고리의 다른 글
    • 영속성 전이(Cascade) ManyToOne 시 주의할점 - 상위 엔티티 삭제 문제
    • Spring Data Jpa Cursor based Pagenation (커서 기반 페이지네이션) 예제 (JpaRepository 커서 기반 페이지네이션
    • JPA Entity의 Field Data Type은 primitive, Wrapper 중 어떤것을 사용해야 할까?
    • JpaRepository에서 save시 select 쿼리가 먼저 실행되는 이유
    ysk(0soo)
    ysk(0soo)
    백엔드 개발을 좋아합니다. java kotlin spring, infra 에 관심이 많습니다. email : kim206gh@naver.com github : https://github.com/devysk

    티스토리툴바