图源:
这篇文章介绍如何在 JPA(Hibernate)中使用属性转换器。
在文章中,我介绍了如何使用@Embedded
和@Embeddable
将一个类型嵌入实体类,并映射表结构中的某几列数据。实际上,在日常开发中,将一些当前表的附加信息单独存储成一个序列化或 JSON 格式的字段是很常见的情况。
下面就演示怎么在 JPA 中这么做。
实体类
首先,看作为示例的实体类:
name = "user_student3")
(chain = true)
(onlyExplicitlyIncluded = 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;
columnDefinition = "text")
( private Contacts contacts;
public Student3() {
}
}
public class Contacts {
private String name;
private String address;
private String phone;
}
其属性contacts
我们不再像之前那样映射到多个字段,而是直接存储到一个字段上,所以这里用@Column(columnDefinition = "text")
进行标注。
考虑到通常的 JSON 串长度以及未来的扩展需要,使用 TEXT 作为对应字段的类型是个常见手段。
也可以使用
@Lob
注解(Large Object)标记属性让其对应的表结构列变成 TEXT 类型,但在数据库是MySQL 的时候,列类型会变成TINY TEXT
而非TEXT
,前者最大长度是255,因此这么做不太合适。
属性转换器
为了能让 Hibernate 知道怎么处理Contacts
类型,我们需要定义一个属性转换器(Attribute Converter),并在转换器中实现具体的转换逻辑:
public class ContactsConverter implements AttributeConverter<Contacts, String> {
public String convertToDatabaseColumn(Contacts attribute) {
ObjectMapper om = new ObjectMapper();
return om.writeValueAsString(attribute);
}
public Contacts convertToEntityAttribute(String dbData) {
if (ObjectUtils.isEmpty(dbData)) {
return null;
}
ObjectMapper om = new ObjectMapper();
return om.readValue(dbData, Contacts.class);
}
}
可以看到,Hibernate 的属性转换器需要使用@Converter
注解并实现AttributeConverter
接口。
转换器中需要实现两个方法,分别对应将具体类型转换为String
以及将String
还原为具体类型。在这个示例中,我们使用 JSON 作为转换的目标字符串格式,通过 Spring 内置的 FastJSON 可以很容易实现这一点。
使用转换器很简单:
// ...
public class Student3 {
// ...
converter = ContactsConverter.class)
( columnDefinition = "text")
( private Contacts contacts;
// ...
}
在相应属性上使用@Convert
注解,并设置具体使用的转换器即可。
测试
测试用例:
void testAddNewStudent() {
Student3 newStudent = Student3.builder()
.address("宁安大街101号")
.name("icexmoon")
.contacts(Contacts.builder()
.name("lalala")
.address("北京东路100号")
.phone("123456789")
.build())
.build();
student3Repository.save(newStudent);
Assertions.assertNotNull(newStudent.getId());
var findStudents = student3Repository.findAllById(List.of(newStudent.getId()));
var findStudent = findStudents.stream().filter(s -> s.getId().equals(newStudent.getId())).findFirst().get();
Assertions.assertEquals(newStudent.getContacts(), findStudent.getContacts());
}
The End,谢谢阅读。
本文的完整示例代码可以在
文章评论