- @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 로 명칭이 바뀌였다.
- 기본 스프링 패키지에 등록되어 있지 않은 이유
@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
@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
이다. - 검사를 한다
- Bean Validation이 클래스패스에 있고, Hibernate Annotation이 사용되면 기본 값은
- 이것도 베스트 프랙티스가 아니라며
@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 emptyHibernate metadata impact
None
@NotNull
Checks that the annotated value is not nullHibernate 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 |