从零开始 Spring Boot 54:@NotNull in Entity

news2025/1/11 0:47:48

从零开始 Spring Boot 54:@NotNull in Entity

spring boot

图源:简书 (jianshu.com)

之前通过两篇文章介绍了 Hibernate Validation 在 Spring 中的应用:

  • 从零开始 Spring Boot 13:参数校验 - 红茶的个人站点 (icexmoon.cn)
  • 从零开始 Spring Boot 30:数据校验 - 红茶的个人站点 (icexmoon.cn)

实际上这些的数据校验注解也可以在 JPA 的实体类中使用,本文会展示如何在实体类中使用@NotNull注解用于数据校验,以及其和@Column注解的异同。

@NotNull

先看实体类:

// ...
public class Student4 {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @EqualsAndHashCode.Include
    private Long id;

    @NotNull
    @Column(length = 25)
    private String name;

    @NotNull
    @Column(length = 50)
    private String address;

    @Convert(converter = ContactsConverter.class)
    @Column(columnDefinition = "text")
    private Contacts contacts;

    public Student4() {

    }
}

这个实体类中的nameaddress属性并没有用之前用过的@Column(nullable=false),而是使用了 Hibernate Validation 的@NotNull注解。

看效果:

@Test
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 {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @EqualsAndHashCode.Include
    private Long id;

    @Column(length = 25, nullable = false)
    private String name;

    @Column(length = 50, nullable = false)
    private String address;

    @Convert(converter = ContactsConverter.class)
    @Column(columnDefinition = "text")
    private Contacts contacts;

    public Student3() {

    }
}

测试用例:

@Test
@SneakyThrows
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,谢谢阅读。

可以从这里获取本文的完整示例代码。

参考资料

  • Hibernate @NotNull vs @Column(nullable = false) | Baeldung

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/704315.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[RocketMQ] Broker asyncSendMessage处理消息以及自动创建Topic (十)

asyncSendMessage方法用来处理来自producer发送的消息。 文章目录 1.asyncSendMessage异步处理单条消息2.preSend准备响应命令对象2.1 msgCheck检查并自动创建topic2.1.1 createTopicInSendMessageMethod创建普通topic2.1.2 createTopicInSendMessageBackMethod创建重试topc2.1…

浅入浅出Java锁

前提 做分布式爬虫时,结合已有的架构,直接对某网站的详情页进行了爬取;尴尬的是,某网站需先采集列表页,之后才能采集详情页;这种防爬手段使用了用户行为监控,行为异常的访问直接就给屏蔽了。 对…

c++11 日期和时间工具-(std::chrono)

链接 std::chrono是C11引入的日期时间处理库&#xff0c;其中包含3种时钟&#xff1a; system_clock&#xff0c;steady_clock&#xff0c;high_resolution_clock。 定义于头文件 <chrono> std::chrono 库 system_clock steady_clock 链接 链接2 每一次调用time_…

解决go install github.com/mattn/goreman@latest安装报错

go install github.com/mattn/goremanlatest报错&#xff1a; [rootlocalhost ~]# go install github.com/mattn/goremanlatest go: github.com/mattn/goremanlatest: module github.com/mattn/goreman: Get "https://proxy.golang.org/github.com/mattn/goreman/v/list&…

现在都在说 Docker 好,为什么我一用就出现这么多问题?查了一宿才解决!

#配置国内源进行docker安装 报错 HTTP Error 404 - Not Found 原因&#xff1a;由于配置国内镜像源时&#xff0c;把地址写错了&#xff0c;导致后面安装docker提示HTTP Error 404 解决方法&#xff1a; 1&#xff09;进入到 /etc/yum.repos.d目录下 cd /etc/yum.repos.d l…

8. 查询每日新用户数

文章目录 题目需求思路一实现一题目来源 题目需求 从用户登录明细表&#xff08;user_login_detail&#xff09;中查询每天的新增用户数&#xff0c;若一个用户在某天登录了&#xff0c;且在这一天之前没登录过&#xff0c;则任务该用户为这一天的新增用户。 期望结果如下&am…

Baumer工业相机堡盟工业相机如何通过BGAPISDK进行定序器编程:根据每次触发信号移动感兴趣区域(C#)

Baumer工业相机堡盟工业相机如何通过BGAPISDK进行定序器编程:根据每次触发信号移动感兴趣区域&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机BGAPISDK和定序器编程的技术背景Baumer工业相机通过BGAPISDK进行定序器编程功能1.引用合适的类文件2.Baumer工业相机通过BG…

采集发布到WordPress自定义参数

Wordpress有自定义设置的参数&#xff0c;一般是用户自行设置&#xff0c;或主题和插件扩展新增的自定义参数&#xff0c;要怎么发布&#xff1f; WordPress主题或插件扩展新增的自定义参数&#xff0c;一般是保存到数据库的wp_postmeta表中。 先去数据库中确定对应自定义参数…

设计消息模块的持久层

一、创建MessageDao类 在 com.example.emos.wx.db.dao 包中创建 MessageDao.java 类 Repository public class MessageDao {Autowiredprivate MongoTemplate mongoTemplate;public String insert(MessageEntity entity){Date sendTimeentity.getSendTime();sendTimeDateUtil.…

【day4】类和对象

#include <iostream> using namespace std;class Complex {int real;int vir; public:Complex(){}Complex(int a,int b):real(a),vir(b){}void show(){cout << real << "" << vir << "i" << endl;}//成员函数版的运算…

Java基础-lambda表达式

简化匿名内部类的书写 下面两种写法均可&#xff1b; Arrays.sort(arr, new Comparator<Integer>() {Overridepublic int compare(Integer o1, Integer o2) {return o1 - o2;} }); Arrays.sort(arr, (Integer o1, Integer o2) -> {return o1 - o2;} );函数式编程思想&…

山西电力市场日前价格预测【2023-07-01】

日前价格预测 预测明日&#xff08;2023-07-01&#xff09;山西电力市场全天平均日前电价为364.57元/MWh。其中&#xff0c;最高日前价格为451.88元/MWh&#xff0c;预计出现在21: 30。最低日前电价为309.59元/MWh&#xff0c;预计出现在13: 30。以上预测仅供学习参考&#xff…

LabVIEW开发工业物联网状态监测

物理对象的网络&#xff0c;允许在它们之间传输数据。信息通常保存在集中式云数据库中。由于物联网&#xff0c;我们现在可以从远处进行监控和感知。由于网络和通信的增加&#xff0c;越来越多的流程可能会自动化。 调度、维护管理和质量改进等关键领域的决策正受到大数据技术…

python接口自动化(六)--发送get请求接口(详解)

简介 如果想用python做接口测试&#xff0c;我们首先有不得不了解和学习的模块。它就是第三方模块&#xff1a;Requests。 虽然Python内置的urllib模块&#xff0c;用于访问网络资源。但是&#xff0c;它用起来比较麻烦&#xff0c;而且&#xff0c;缺少很多实用的高级功能。更…

Git入门级指南

Git入门级指南 在软件开发和版本控制中&#xff0c;Git是一种非常流行且强大的工具。本文将为你提供关于Git的基本知识&#xff0c;并提 供一些实例来演示如何正确使用Git来管理代码。 关于git的简介 Git是一种分布式版本控制系统&#xff0c;它可以跟踪和管理项目中的代码…

前端安全问题及解决方案

随着互联网的高速发展&#xff0c;信息安全问题已经成为行业最为关注的焦点之一。总的来说安全是很复杂的一个领域&#xff0c;在移动互联网时代&#xff0c;前端人员除了传统的 XSS、CSRF 等安全问题之外&#xff0c;还时常遭遇网络劫持、非法调用 Hybrid API 等新型安全问题。…

(2023最新)互联网1010道Java面试真题汇总

我相信各位小伙伴们都发现了&#xff0c;现在的 IT 的环境并不如以前了&#xff0c;似乎是迎来“寒冬”&#xff0c;再加上最近上热搜的阿里云大裁员事件&#xff0c;又将 Java 开发岗推上了一个新的难度。而被裁员的人&#xff0c;不得不降薪重新找到一份工作&#xff0c;而经…

Django框架-5

路由系统 通过URL&#xff08;Uniform Resource Locator&#xff0c;统一资源定位符&#xff09;可以访问互联网上的资源——用户通过 浏览器向指定URL发起请求&#xff0c;Web服务器接收请求并返回用户请求的资源&#xff0c;因此可以将URL视为用户与服务器之间交互的桥梁。 …

MATLAB中scatter函数用法

目录 语法 说明 ​示例 scatter函数的功能是绘制散点图。 语法 scatter(x,y) scatter(x,y,sz) scatter(x,y,sz,c) scatter(___,"filled") scatter(___,mkr) scatter(tbl,xvar,yvar) scatter(tbl,xvar,yvar,"filled") scatter(ax,___) scatter(___,Nam…

Mybatis面试题--MyBatis延迟加载

Mybatis是否支持延迟加载&#xff1f; 答&#xff1a;Mybatis支持延迟记载&#xff0c;但默认没有开启 什么叫做延迟加载&#xff1f; 查询用户的时候&#xff0c;把用户所属的订单数据也查询出来&#xff0c;这个是立即加载 查询用户的时候&#xff0c;暂时不查询订单数据&…