从零开始 Spring Boot 53:JPA 属性转换器
图源:简书 (jianshu.com)
这篇文章介绍如何在 JPA(Hibernate)中使用属性转换器。
在前篇文章中,我介绍了如何使用@Embedded
和@Embeddable
将一个类型嵌入实体类,并映射表结构中的某几列数据。实际上,在日常开发中,将一些当前表的附加信息单独存储成一个序列化或 JSON 格式的字段是很常见的情况。
下面就演示怎么在 JPA 中这么做。
实体类
首先,看作为示例的实体类:
@AllArgsConstructor
@Builder
@Data
@Entity
@Table(name = "user_student3")
@Accessors(chain = true)
@EqualsAndHashCode(onlyExplicitlyIncluded = 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;
@Column(columnDefinition = "text")
private Contacts contacts;
public Student3() {
}
}
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@EqualsAndHashCode
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),并在转换器中实现具体的转换逻辑:
@Converter
public class ContactsConverter implements AttributeConverter<Contacts, String> {
@Override
@SneakyThrows
public String convertToDatabaseColumn(Contacts attribute) {
ObjectMapper om = new ObjectMapper();
return om.writeValueAsString(attribute);
}
@Override
@SneakyThrows
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 {
// ...
@Convert(converter = ContactsConverter.class)
@Column(columnDefinition = "text")
private Contacts contacts;
// ...
}
在相应属性上使用@Convert
注解,并设置具体使用的转换器即可。
测试
测试用例:
@Test
@SneakyThrows
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,谢谢阅读。
本文的完整示例代码可以在这里获取。
参考资料
- JPA Attribute Converters | Baeldung
- [JPA Annotation for the PostgreSQL TEXT Type | Baeldung](https://www.baeldung.com/jpa-annotation-postgresql-text-type#:~:text=Using the %40Lob annotation on the description field%2C,the %40Column annotation%2C together with the columnDefinition property.)
- MySQL text类型 - MySQL教程 (yiibai.com)