图源:
JPA 是一个 ORM 框架,因此,通常我们需要在实体类中定义表结构,这其中就包含可能的字段默认值。
本文介绍如何在 Hibernate(JPA)中设置默认列值(Default Column Value)。
默认属性值
最简单的方式是对实体类指定一个默认的属性值,比如:
name = "USER_TREE")
(
public class Tree {
strategy = GenerationType.AUTO)
( private Long id;
nullable = false)
( private Integer age = 5;
}
测试用例:
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
通过@Column
的columnDefinition
属性,我们可以指定表结构的字段定义,可以利用这一点指定 DDL 语句中的字段默认值:
name = "USER_TEACHER")
(
public class Teacher {
strategy = GenerationType.AUTO)
( private Long id;
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
属性中的表字段默认值,并像我们期待的那样在添加实体实例时自动使用:
void testAddTeacherWithDefaultValue() {
Teacher teacher = new Teacher();
teacherRepository.save(teacher);
var findTeacher = teacherRepository.findAll().stream().findFirst().get();
Assertions.assertNull(findTeacher.getName());
}
这个示例中添加到数据库中的Teacher
实际上其name
是null
。
@DynamicInsert
可以使用@@DynamicInsert
解决上述问题,这个注解可以让实体的 INSERT SQL 排除值为null
的字段:
// ...
public class Teacher {
// ...
}
测试用例:
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 字段的默认值。
但这样做依然有一个问题——不会体现在我们用于插入的实体上,需要重新从数据库加载实体才行:
void testAddTeacherWithDefaultValue2() {
Teacher teacher = new Teacher();
teacherRepository.save(teacher);
Assertions.assertNull(teacher.getName());
// ...
}
可以看到,Hibernate 并不会在这种情况下帮助我们自动重新加载实体(以获取通过数据库指定的字段默认值)。
默认属性+columnDefinition
更合理的做法是结合默认属性以及用columnDefinition
指定 DDL 字段默认值:
name = "USER_STUDENT")
(chain = true)
(public class Student {
strategy = GenerationType.AUTO)
( Exclude
. private Long id;
public static final String DEFAULT_NAME = "icexmoon";
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);
AccessLevel.NONE)
( name = "BIRTH_DAY", columnDefinition = "date DEFAULT '2002-01-01'")
( private LocalDate birthDay = DEFAULT_BIRTH_DAY;
AccessLevel.NONE)
( AccessLevel.NONE)
( Exclude
.
private Integer age;
public static final Gender DEFAULT_GENDER = Gender.MALE;
EnumType.ORDINAL)
( name = "gender", columnDefinition = "tinyint DEFAULT '0'")
( private Gender gender = DEFAULT_GENDER;
// ...
}
这样做其实依然有个缺陷,如果要修改属性默认值,最好同时修改
columnDefinition
中的default
值,否则就会出现语义上的不一致。我有尝试过在指定columnDefinition
值时用表达式指定默认值,但只能使用常量表达式,无法实现复杂语义。
测试用例:
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,谢谢阅读。
可以从
文章评论