JPA属性转换器的使用与实例解析
在Java持久化框架中,JPA(Java Persistence API)为我们提供了强大的功能来操作数据库。其中,属性转换器(Attribute Converter)是一个非常实用的特性,它允许我们将实体类中的属性类型转换为适合存储在数据库中的类型。通过实现javax.persistence.AttributeConverter
接口并使用@Converter
注解,我们可以自定义属性的存储和加载方式。本文将通过一个具体的例子来详细解析JPA属性转换器的使用。
一、属性转换器的基本概念
在JPA中,实体类的某些属性可能无法直接映射到数据库的某种数据类型。例如,java.io.File
对象无法直接存储为数据库中的字符串或二进制数据。此时,属性转换器就可以派上用场。它允许我们将File
对象转换为字符串路径(String
类型),并在加载时将字符串路径重新转换为File
对象。
二、实现属性转换器
属性转换器需要实现AttributeConverter
接口,并重写convertToDatabaseColumn
和convertToEntityAttribute
两个方法。convertToDatabaseColumn
方法用于将实体类中的属性转换为数据库存储的类型,而convertToEntityAttribute
方法则用于将数据库中的数据转换回实体类的属性类型。
以下是一个具体的例子:
@Converter
public class FileConverter implements AttributeConverter<File, String> {
@Override
public String convertToDatabaseColumn(File attribute) {
return attribute.getAbsolutePath(); // 将File对象转换为绝对路径字符串
}
@Override
public File convertToEntityAttribute(String dbData) {
return new File(dbData); // 将字符串路径重新转换为File对象
}
}
三、在实体类中使用属性转换器
在实体类中,我们可以通过@Convert
注解来指定某个属性使用我们定义的转换器。以下是一个包含File
属性的Report
实体类:
@Entity
public class Report {
@Id
@GeneratedValue
private long id;
private String description;
@Convert(converter = FileConverter.class)
private File file;
// 省略getter和setter方法
}
四、完整示例代码
以下是一个完整的示例代码,展示了如何使用属性转换器来持久化和加载包含File
属性的Report
实体。
@Entity
public class Report {
@Id
@GeneratedValue
private long id;
private String description;
@Convert(converter = FileConverter.class)
private File file;
// 省略getter和setter方法
}
@Converter
public class FileConverter implements AttributeConverter<File, String> {
@Override
public String convertToDatabaseColumn(File attribute) {
return attribute.getAbsolutePath();
}
@Override
public File convertToEntityAttribute(String dbData) {
return new File(dbData);
}
}
public class ExampleMain {
private static EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("example-unit");
public static void main(String[] args) {
try {
nativeQuery("Show Columns from Report");
persistEntity();
nativeQuery("Select * from Report");
findEntity();
} finally {
entityManagerFactory.close();
}
}
private static void findEntity() {
EntityManager em = entityManagerFactory.createEntityManager();
Report report = em.find(Report.class, 1L);
System.out.println("Report loaded: " + report);
em.close();
}
public static void persistEntity() {
Report report = new Report();
report.setDescription("test report");
report.setFile(new File("c:/temp/report-details.txt"));
System.out.println("Persisting report: " + report);
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
em.persist(report);
em.getTransaction().commit();
em.close();
}
public static void nativeQuery(String s) {
EntityManager em = entityManagerFactory.createEntityManager();
System.out.printf("'%s'%n", s);
Query query = em.createNativeQuery(s);
List list = query.getResultList();
for (Object o : list) {
if (o instanceof Object[]) {
System.out.println(Arrays.toString((Object[]) o));
} else {
System.out.println(o);
}
}
em.close();
}
}
五、运行结果
运行上述代码后,我们可以看到以下输出:
'Show Columns from Report'
[ID, BIGINT(19), NO, PRI, NULL]
[DESCRIPTION, VARCHAR(255), YES, , NULL]
[FILE, VARCHAR(255), YES, , NULL]
Persisting report: Report{id=0, description='test report', file=c:\temp\report-details.txt}
'Select * from Report'
[1, test report, c:\temp\report-details.txt]
Report loaded: Report{id=1, description='test report', file=c:\temp\report-details.txt}
从结果可以看出,File
对象被成功转换为字符串路径并存储在数据库中,加载时又正确地转换回了File
对象。
六、不使用@Converter的情况
如果不使用@Converter
注解,File
对象将被以二进制形式存储,这可能不是我们期望的结果。以下是不使用@Converter
的情况:
@Entity
public class Report {
@Id
@GeneratedValue
private long id;
private String description;
private File file; // 不使用@Convert注解
// 省略getter和setter方法
}
运行结果如下:
'Show Columns from Report'
[ID, BIGINT(19), NO, PRI, NULL]
[DESCRIPTION, VARCHAR(255), YES, , NULL]
[FILE, VARBINARY(255), YES, , NULL]
Persisting report: Report{id=0, description='test report', file=c:\temp\report-details.txt}
'Select * from Report'
[1, test report, [B@18518ccf]
Report loaded: Report{id=1, description='test report', file=c:\temp\report-details.txt}
可以看到,File
对象被存储为二进制数据,这使得数据的可读性变差。
七、总结
通过本文的介绍,我们了解了JPA属性转换器的基本概念、实现方式以及如何在实体类中使用它。属性转换器为我们提供了灵活的机制,可以将复杂的对象类型转换为适合存储在数据库中的类型,同时也方便了数据的加载和使用。在实际开发中,合理使用属性转换器可以大大提高代码的可读性和可维护性。