从零开始 Spring Boot 51:JPA 中的默认列值

news2024/11/18 3:25:42

从零开始 Spring Boot 51:JPA 中的默认列值

spring boot

图源:简书 (jianshu.com)

JPA 是一个 ORM 框架,因此,通常我们需要在实体类中定义表结构,这其中就包含可能的字段默认值。

本文介绍如何在 Hibernate(JPA)中设置默认列值(Default Column Value)。

默认属性值

最简单的方式是对实体类指定一个默认的属性值,比如:

@Data
@Table(name = "USER_TREE")
@Entity
public class Tree {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private Integer age = 5;
}

测试用例:

@Test
void testAddTreeWithDefaultValue(){
    Tree tree = new Tree();
    treeRepository.save(tree);
    Assertions.assertEquals(5, tree.getAge());
}

这样做的缺点是由 Hibernate 自动生成的表结构中并不会体现字段的默认值:

CREATE TABLE `user_tree` (
  `id` bigint NOT NULL,
  `age` int NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

一般来说是不会产生什么影响的,但如果我们直接在数据库中执行 INSERT SQL,并且期望对拥有默认值的字段使用缺省,就无法实现。

columnDefinition

通过@ColumncolumnDefinition属性,我们可以指定表结构的字段定义,可以利用这一点指定 DDL 语句中的字段默认值:

@Data
@Table(name = "USER_TEACHER")
@Entity
public class Teacher {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(columnDefinition = "varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'icexmoon'")
    private String name;
}

Hibernate 生成的表结构中的确会出现字段的默认值:

CREATE TABLE `user_teacher` (
  `id` bigint NOT NULL,
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'icexmoon',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

但遗憾的是,Hibernate 并不能识别columnDefinition属性中的表字段默认值,并像我们期待的那样在添加实体实例时自动使用:

@Test
void testAddTeacherWithDefaultValue() {
    Teacher teacher = new Teacher();
    teacherRepository.save(teacher);
    var findTeacher = teacherRepository.findAll().stream().findFirst().get();
    Assertions.assertNull(findTeacher.getName());
}

这个示例中添加到数据库中的Teacher实际上其namenull

@DynamicInsert

可以使用@@DynamicInsert解决上述问题,这个注解可以让实体的 INSERT SQL 排除值为null的字段:

@DynamicInsert
// ...
public class Teacher {
	// ...
}

测试用例:

@Test
void testAddTeacherWithDefaultValue() {
    Teacher teacher = new Teacher();
    teacherRepository.save(teacher);
    var findTeacher = teacherRepository.findAll().stream().findFirst().get();
    Assertions.assertNotNull(findTeacher.getName());
    Assertions.assertEquals("icexmoon", findTeacher.getName());
}

可以看到此时的 Hibernate SQL 日志:

Hibernate: insert into user_teacher (id) values (?)

所以自然而然的,插入的数据中name字段使用了 DDL name 字段的默认值。

但这样做依然有一个问题——不会体现在我们用于插入的实体上,需要重新从数据库加载实体才行:

@Test
void testAddTeacherWithDefaultValue2() {
    Teacher teacher = new Teacher();
    teacherRepository.save(teacher);
    Assertions.assertNull(teacher.getName());
    // ...
}

可以看到,Hibernate 并不会在这种情况下帮助我们自动重新加载实体(以获取通过数据库指定的字段默认值)。

默认属性+columnDefinition

更合理的做法是结合默认属性以及用columnDefinition指定 DDL 字段默认值:

@Getter
@Setter
@Entity
@ToString
@EqualsAndHashCode
@Table(name = "USER_STUDENT")
@Accessors(chain = true)
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @EqualsAndHashCode.Exclude
    private Long id;

    public static final String DEFAULT_NAME = "icexmoon";
    @Column(name = "NAME", columnDefinition = "varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'icexmoon'")
    private String name = DEFAULT_NAME;

    public static final LocalDate DEFAULT_BIRTH_DAY = LocalDate.of(2022, 1, 1);
    @Setter(AccessLevel.NONE)
    @Column(name = "BIRTH_DAY", columnDefinition = "date DEFAULT '2002-01-01'")
    private LocalDate birthDay = DEFAULT_BIRTH_DAY;

    @Transient
    @Setter(AccessLevel.NONE)
    @Getter(AccessLevel.NONE)
    @EqualsAndHashCode.Exclude
    @Nullable
    private Integer age;

    public static final Gender DEFAULT_GENDER = Gender.MALE;
    @Enumerated(EnumType.ORDINAL)
    @Column(name = "gender", columnDefinition = "tinyint DEFAULT '0'")
    private Gender gender = DEFAULT_GENDER;
    // ...
}

这样做其实依然有个缺陷,如果要修改属性默认值,最好同时修改columnDefinition中的default值,否则就会出现语义上的不一致。我有尝试过在指定columnDefinition值时用表达式指定默认值,但只能使用常量表达式,无法实现复杂语义。

测试用例:

@Test
void testNewStudentWithDefaultValue() {
    Student student = new Student();
    studentRepository.save(student);
    Assertions.assertEquals(Student.DEFAULT_NAME, student.getName());
    Assertions.assertEquals(Student.DEFAULT_BIRTH_DAY, student.getBirthDay());
    Assertions.assertEquals(Student.DEFAULT_GENDER, student.getGender());
    var findStudent = studentRepository.findOne(Example.of(new Student().setName(Student.DEFAULT_NAME))).get();
    Assertions.assertNotNull(findStudent);
    Assertions.assertEquals(Student.DEFAULT_NAME, findStudent.getName());
    Assertions.assertEquals(Student.DEFAULT_BIRTH_DAY, findStudent.getBirthDay());
    Assertions.assertEquals(Student.DEFAULT_GENDER, findStudent.getGender());
}

The End,谢谢阅读。

可以从这里获取本文的完整测试用例。

参考资料

  • Default Column Values in JPA | Baeldung
  • hibernate设置默认值

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

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

相关文章

HBase(6):计数操作

1 需求 查看HBase中的ORDER_INFO表,一共有多少条记录。 2 count命令 count命令专门用来统计一个表中有多少条数据。语法: count 表名 注意:这个操作是比较耗时的。在数据量大的这个命令可能会运行很久,真实环境不要使用该命令。…

【从零开始学习JAVA | 第二十五篇】泛型

目录 前言: 泛型: 额外拓展: 总结: 前言: 本文将详细介绍之前我们在JAVA 中一直在讲的泛型,各位感兴趣的同学可以点击进来观看。 泛型: 泛型是一种编程概念,它允许在定义类、接…

文章测试

Markdown示例 本文件的源码是一个markdown文件,也就是说在本工程中直接添加markdown即可嵌入到sphinx文档中。 关于使sphinx支持markdown的详细配置说明,请参考文档markdown-sphinx。 markdown的公式语法在sphinx可能不支持。 以下是markdown的语法使用示例 文…

PDF如何转换成Word?PDF转Word方法分享!​

PDF大家都不陌生了吧?作为打工人,学生党的大家都知道,PDF是现在不可或缺的文件传输工具之一,不仅可将文档转为Word,还可以转成excel,ppt等各种形式,其重要性不言而喻,那么今天小编就跟大家具体说…

Web自动化元素定位之xpath定位详解

Web自动化常见的定位方式 为什么要学习定位 1.让程序操作指定元素,就必须先找到此元素 2.程序不像人类用眼睛直接定位到元素 webDriver提供了八种定位元素的方式 定位方式总结 1.id、name、class_name、tag_name:根据元素的标签或元素的属性来进行定位 2.link_t…

gitLab配置ssh实现私钥访问

1.配置ssh文件 1.cd C:\Users\用户名\.ssh 找到文件夹 删除.ssh 里面所有其他文件方面我们配置要最新的 2.win r cmd 呼出命令行 ssh-keygen -t rsa -C "必须对应gitLab用户名" 3.生成文件夹拿到ssh 4.复制id_rsa_pub 文件的全部字符串 公钥给到GitLab服务器 2.公…

使用 JavaScript 在没有插件的情况下输入文本掩码

文章目录 JavaScript 中信用卡号的输入掩码JavaScript 中的邮政编码输入掩码在 JavaScript 中使用括号输入掩码的电话号码为 JavaScript 中的不同字段自定义输入掩码 JavaScript 输入掩码或掩码文本框是一种控件,它为用户提供了一种简单可靠的方式来收集基于标准掩码…

【Servlet学习三】实现一个内存版本的简易计算器~

目录 一、方式1:使用form表单的形式(不推荐) 🌈1、前端代码:HTML文件 🌈2、后端代码:Calculator_form.java文件 🌈3、最终效果 二、方式2:使用ajax形式(…

力扣 105. 从前序与中序遍历序列构造二叉树

题目来源:https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/ C题解:前序遍历是中左右,中序遍历是左中右,所以拿到两个遍历数组,我们可以从前序遍历首节点获取中间…

Docker 私有仓库

一、私有仓库搭建 拉取私有仓库镜像 docker pull registry 启动私有仓库 docker run -id --nameprivate_registry -p 5000:5000 registry 打开浏览器输入 http://私有仓库服务器IP地址:5000/v2/_catalog 修改 daemon.json 文件 sudo gedit /etc/docker/daemon.json 在…

在Mapper.xml中写复杂的动态SQL语句

说明:在三层架构开发中,使用Mybatis框架操作数据库有两种方式,一种是在Mapper类里的方法上加注解(Select、Insert等),另一种是在Mapper.xml文件的标签内写SQL语句。第二种方式相比第一种,具有更…

electron webview 页面加载事件顺序

electron webview 页面加载事件顺序 1.did-start-loading 页面开始加载web 2.load-commit 主页面文档加载框架 3.page-title-updated titledom 4.dom-ready 主页面 dom 加载完成electron 5.load-commit frame文档加载.net 6.did-frame-finish-load frame 加载完成cdn 7.d…

Springboot钉钉免密登录集成(钉钉小程序和H5微应用)

欢迎访问我的个人博客:www.ifueen.com RT,因为业务需要把我们系统集成到钉钉里面一个小程序和一个H5应用,并且在钉钉平台上面实现无感登录,用户打开我们系统后不需要再输入密码即可登录进系统,查阅文档实际操作过之后记录一下过程…

HTML转EXE工具(HTML App Build)永久免费版

HTML转EXE工具(HTM2EXE)在CSDN上发布时间轴: 序号时间链接12022-08-17HTML转EXE工具(HTML App Build)初始版22023-02-18HTML转EXE工具(HTML App Build)最新版32023-06-23(实际未发布…

高德地图点聚合报错

最近做项目遇到这样一个玄学问题: 在写高德地图的时候加一个覆盖物 除了金华市其他城市覆盖并且不能点击,金华市内点击获取坐标点这样一个业务, 到这里都很正常,代码奉上 const AMap window.AMapconst state reactive({mapInp…

黑客进行网络欺骗攻击的手段有哪些?

网络欺骗主要方式有:IP 欺骗、ARP 欺骗、DNS 欺骗、Web 欺骗、电子邮件欺骗、源路由欺骗和地址欺骗等。其技术主要分为 HONEYPOT、分布式 HONEYPOT 和欺骗空间技术等。其中,源路由欺骗方式指通过指定路由,以假冒身份与其他主机进行合法通信或…

python实现遗传算法(单目标优化,多目标优化)

首先我们应该熟悉numpy的矩阵取值 [:, 1::2] # 第一维,从第二个元素起步长为2取元素[:, ::2] # 第一维,从第一个元素起步长为2取元素 1 遗传本质 染色体的交叉和变异 种群:很多个个体组成的群体,即为所有可能解的集合个体&…

[muduo学习笔记]事件分发器(Channel、Poller)

此学习笔记参考施磊老师的muduo教学课程。 目的是搞懂 muduo 网络库的核心框架。EventLoop、channel 和 Poller 之间的关系 文章目录 1 Poller 抽象基类2 Channel3 模块的包含muduo模块梳理参考: 整体框架如下: muduo是基于 Reactor 模式的网络库&#…

vue中v-for重复数据处理||element下拉框去除重复

前端去重方法有多种,只说三种常用的(新老方法都有) 1-使用常规双for循环(暴力算法)遍历比较的方式对值进行比较 2-使用js方法sort排序(只针对数组),但是经常在vue等新框架中提示TypeError: arr.sort is not a function 3-使用reduce方法(>…

传统后端漏洞----(Web Server) 解析漏洞

笔记 前言IIS解析漏洞文件夹解析漏洞原理限制条件 ";" 分号截断漏洞原理 IIS解析漏洞检测IIS 文件夹解析漏洞检测IIS 分号截断漏洞检测 防御手段 Nginx解析漏洞Nginx 文件类型错误解析漏洞导致任意PHP代码执行原理Nginx 空字节解析漏洞导致任意文件可解析&#xff08…