從零開始 Spring Boot 51:JPA 中的默認(rèn)列值
圖源:簡書 (jianshu.com)
JPA 是一個(gè) ORM 框架,因此,通常我們需要在實(shí)體類中定義表結(jié)構(gòu),這其中就包含可能的字段默認(rèn)值。
本文介紹如何在 Hibernate(JPA)中設(shè)置默認(rèn)列值(Default Column Value)。
默認(rèn)屬性值
最簡單的方式是對實(shí)體類指定一個(gè)默認(rèn)的屬性值,比如:
@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());
}
這樣做的缺點(diǎn)是由 Hibernate 自動(dòng)生成的表結(jié)構(gòu)中并不會(huì)體現(xiàn)字段的默認(rèn)值:
CREATE TABLE `user_tree` (
`id` bigint NOT NULL,
`age` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
一般來說是不會(huì)產(chǎn)生什么影響的,但如果我們直接在數(shù)據(jù)庫中執(zhí)行 INSERT SQL,并且期望對擁有默認(rèn)值的字段使用缺省,就無法實(shí)現(xiàn)。
columnDefinition
通過@Column
的columnDefinition
屬性,我們可以指定表結(jié)構(gòu)的字段定義,可以利用這一點(diǎn)指定 DDL 語句中的字段默認(rèn)值:
@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 生成的表結(jié)構(gòu)中的確會(huì)出現(xiàn)字段的默認(rèn)值:
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 并不能識(shí)別columnDefinition
屬性中的表字段默認(rèn)值,并像我們期待的那樣在添加實(shí)體實(shí)例時(shí)自動(dòng)使用:
@Test
void testAddTeacherWithDefaultValue() {
Teacher teacher = new Teacher();
teacherRepository.save(teacher);
var findTeacher = teacherRepository.findAll().stream().findFirst().get();
Assertions.assertNull(findTeacher.getName());
}
這個(gè)示例中添加到數(shù)據(jù)庫中的Teacher
實(shí)際上其name
是null
。
@DynamicInsert
可以使用@@DynamicInsert
解決上述問題,這個(gè)注解可以讓實(shí)體的 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());
}
可以看到此時(shí)的 Hibernate SQL 日志:
Hibernate: insert into user_teacher (id) values (?)
所以自然而然的,插入的數(shù)據(jù)中name
字段使用了 DDL name 字段的默認(rèn)值。
但這樣做依然有一個(gè)問題——不會(huì)體現(xiàn)在我們用于插入的實(shí)體上,需要重新從數(shù)據(jù)庫加載實(shí)體才行:
@Test
void testAddTeacherWithDefaultValue2() {
Teacher teacher = new Teacher();
teacherRepository.save(teacher);
Assertions.assertNull(teacher.getName());
// ...
}
可以看到,Hibernate 并不會(huì)在這種情況下幫助我們自動(dòng)重新加載實(shí)體(以獲取通過數(shù)據(jù)庫指定的字段默認(rèn)值)。
默認(rèn)屬性+columnDefinition
更合理的做法是結(jié)合默認(rèn)屬性以及用columnDefinition
指定 DDL 字段默認(rèn)值:
@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;
// ...
}
這樣做其實(shí)依然有個(gè)缺陷,如果要修改屬性默認(rèn)值,最好同時(shí)修改
columnDefinition
中的default
值,否則就會(huì)出現(xiàn)語義上的不一致。我有嘗試過在指定columnDefinition
值時(shí)用表達(dá)式指定默認(rèn)值,但只能使用常量表達(dá)式,無法實(shí)現(xiàn)復(fù)雜語義。
測試用例:
@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,謝謝閱讀。文章來源:http://www.zghlxwxcb.cn/news/detail-510945.html
可以從這里獲取本文的完整測試用例。文章來源地址http://www.zghlxwxcb.cn/news/detail-510945.html
參考資料
- Default Column Values in JPA | Baeldung
- hibernate設(shè)置默認(rèn)值
到了這里,關(guān)于從零開始 Spring Boot 51:JPA 中的默認(rèn)列值的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!