图源:
之前通过两篇文章介绍了 Hibernate Validation 在 Spring 中的应用:
实际上这些的数据校验注解也可以在 JPA 的实体类中使用,本文会展示如何在实体类中使用@NotNull
注解用于数据校验,以及其和@Column
@NotNull
先看实体类:
// ...
public class Student4 {
strategy = GenerationType.AUTO)
( Include
. private Long id;
length = 25)
( private String name;
length = 50)
( private String address;
converter = ContactsConverter.class)
( columnDefinition = "text")
( private Contacts contacts;
public Student4() {
}
}
这个实体类中的name
和address
属性并没有用之前用过的@Column(nullable=false)
,而是使用了 Hibernate Validation 的@NotNull
注解。
看效果:
void addStudentWithValidation() {
Student4 s = new Student4.Student4Builder()
.build();
Assertions.assertThrows(TransactionSystemException.class, () -> {
student4Repository.save(s);
});
}
如果添加实体的时候用@Notnull
标记的属性为null
,就会抛出一个TransactionSystemException
异常,并提示"xxx不能为null"。通过观察输出的日志可以发现,并没有 Hibernate 执行 INSERT SQL 的相关日志,说明根本没有执行相关的 SQL 就已经退出。
实际上 Hibernate 会在@PrePersist
事件中对使用 Hibernate Validation 相关注解的属性进行验证,如果不通过,就抛出异常并终止。
实际上使用@NotNull
注解还有一个额外好处——Hibernate 可以帮助我们在 DDL 中将相应的字段设置为not null
:
CREATE TABLE `user_student4` (
`id` bigint NOT NULL,
`address` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
`contacts` text COLLATE utf8mb4_general_ci,
`name` varchar(25) COLLATE utf8mb4_general_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
如果你不需要让 Hibernate 自动使用 Validation 相关注解修改 DDL 语句,可以:
spring.jpa.properties.hibernate.validator.apply_to_ddl=false
现在由 Hibernate 自动生成的 DDL 中就不包含相应字段的not null
了:
CREATE TABLE `user_student4` (
`id` bigint NOT NULL,
`address` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
`contacts` text COLLATE utf8mb4_general_ci,
`name` varchar(25) COLLATE utf8mb4_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
比较奇怪的是有一个
DEFAULT NULL
。
@Column(nullable=false)
我们知道,使用@Column(nullable=false)
会让生成的 DDL 语句中包含not null
,并在 Hibernate 执行 INSERT SQL 时才会发挥作用(如果相应的字段为null
就会报错)。
实际上这并不完全正确,只能说默认的情况如此。但是,我们可以通过设置属性让 Hibernate 在执行 INSERT 语句前也进行类似的数据校验:
spring.jpa.properties.hibernate.check_nullability=true
实体类:
// ...
public class Student3 {
strategy = GenerationType.AUTO)
( Include
. private Long id;
length = 25, nullable = false)
( private String name;
length = 50, nullable = false)
( private String address;
converter = ContactsConverter.class)
( columnDefinition = "text")
( private Contacts contacts;
public Student3() {
}
}
测试用例:
void testAddNewStudentWithNull() {
Student3 newStudent = Student3.builder()
.address("宁安大街101号")
.contacts(Contacts.builder()
.name("lalala")
.address("北京东路100号")
.phone("123456789")
.build())
.build();
Assertions.assertThrows(DataIntegrityViolationException.class, () -> {
student3Repository.save(newStudent);
});
}
此时在保存时,如果相应属性为null
,会抛出一个DataIntegrityViolationException
异常,并提示"xxx属性不能为null"。并且同样的,没有相应的 INSERT SQL 执行日志。
总结
可以看到,@NotNull
和@Column(nullable=false)
都可以起到相同的效果——在执行 SQL 前检查实体对象的相应属性是非为null,如果是就抛出异常。
相比较而言,使用@NotNull
更方便一些(不用修改任何属性),并且我们可以使用 Hibernate Validation 的一系列注解来构建我们的验证逻辑,以确保保存到数据库的内容的合法性。
The End,谢谢阅读。
可以从获取本文的完整示例代码。
文章评论