欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

如何实现属性转换器以支持自定义类型(附示例)

最编程 2024-03-27 21:58:18
...

支持的转换和限制

AttributeConverter的总体概念很简单。AttributeConverter 接口的2个方法定义了2个转换。一个是将用于实体属性的类型转换为JDBC驱动在数据库中插入或更新记录时处理的类型。另一个是将JDBC驱动从数据库中读取记录时返回的类型转换为实体属性的类型。

基于这个简单的概念,属性转换器的能力和限制变得很明显。

你可以在所有映射到你的表模型中的1列的基本属性上使用它,这些属性由实体类、映射的超类或可嵌入类定义。

但是转换器不能处理更复杂的类型,比如整个*ElementCollectionto-many关联,或者任何你想映射到多个数据库列的属性。你也不能在主键属性或版本属性上使用AttributeConverter* 。JPA规范为这些属性定义了一个特定的处理方法,这可能会导致冲突。而那些被*@Temporal@Enumerated注释的属性也不被支持。这是因为这些注释已经定义了对数据库列的映射。你需要决定是使用AttributeConverter*还是其他类型的映射,并且只添加相应的注释。

你不能使用AttributeConverter的情况列表可能看起来比你可以使用它的情况要长得多。但是不要担心,AttributeConverter 非常有用,它可以处理几乎所有的标准用例。

实现一个AttributeConverter

让我们实现一个AttributeConverter ,在java.awt.Color类型的实体属性和包含6位数十六进制值的字符串 之间进行转换。

实现AttributeConverter需要一个实现javax.persistence.AttributeConverter(JPA 1 & 2)或jakarta.persistence.AttributeConverter(JPA 3)接口的类。除了包的名字,这两个接口是相同的。正如你在代码片段中看到的,AttributeConverter接口使用了泛型。这些是实体属性的类型和由JDBC驱动处理的类型。在这个例子中,属性将是Color 类型,JDBC驱动将处理一个String

@Converter(autoApply = true)
public class ColorConverter implements AttributeConverter<Color, String> {

    Logger log = LogManager.getLogger(this.getClass().getName());

    @Override
    public String convertToDatabaseColumn(Color attribute) {
        String hex = "#"+Integer.toHexString(attribute.getRGB()).substring(0,6);
        log.info("Convert "+attribute+" to "+hex);
        return hex;
    }

    @Override
    public Color convertToEntityAttribute(String dbData) {
        Color color = Color.decode(dbData);
        log.info("Convert "+dbData+" to "+color);
        return color;
    }
}

你还需要用JPA的*@Converter注解来注释你的转换器类。@Converter注解告诉你的持久化提供者,比如说Hibernate,这是一个属性转换器。如果你想对所有颜色类型的实体属性使用这个转换器,你可以把它的autoApply* 属性设置为true。如果你不想这么做,请查看下面的章节,在那里我将向你展示如何为一个特定的属性激活转换器。

AttributeConverter 的实现是非常简单的。该接口定义了方法convertToDatabaseColumnconvertToEntityAttribute。Hibernate和任何其他JPA实现都会调用这些方法,将实体属性的值转换为JDBC驱动处理的类型,反之亦然。

激活一个AttributeConverter

你可以通过两种方式激活AttributeConverter

  1. 最简单的是将*@Converter注解的autoApply*属性设置为true。然后你的持久化提供者将对所有给定类型的实体属性使用该转换器。
  2. 或者你可以用javax.persistence.Convert(JPA 1 & 2)或jakarta.persistence.Convert(JPA 3)注解来注解一个实体属性,并引用你的AttributeConverter实现。然后你的持久化提供者只使用该属性的转换器。
    下面的代码片断显示了这种方法的一个例子:
@Entity
public class Rectangle {

    @Id
    @GeneratedValue
    private Integer id;

    private Integer x;

    private Integer y;

    @Convert(converter = ColorConverter.class)
    private Color color;

    ...
}

这就是你需要做的,实现一个提供自定义类型映射的AttributeConverter

该转换器被透明地使用

在你为一个属性激活AttributeConverter后,你的持久化提供者会在所有影响该实体属性的操作中透明地使用该转换器。这包括对该实体类进行的所有读和写操作以及与该属性比较的所有绑定参数。

你可以在下面的例子中看到这一点。它读取了一个颜色为白色的Rectangle 实体对象,并将其颜色改为黑色:

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

Rectangle r = em.createQuery("SELECT r FROM Rectangle r WHERE r.color = :color", Rectangle.class)
				.setParameter("color", Color.WHITE)
				.getSingleResult();

r.setColor(Color.BLACK);

em.getTransaction().commit();
em.close();

我使用Hibernate作为我的JPA实现,用于下面的日志输出,并激活了我推荐的开发系统的日志配置。你可以在日志文件中看到执行的SQL语句和AttributeConverter实现写的信息:

19:11:37,114 INFO  [com.thorben.janssen.model.ColorConverter] - Convert java.awt.Color[r=255,g=255,b=255] to #ffffff
19:11:37,170 DEBUG [org.hibernate.SQL] - select r1_0.id,r1_0.color,r1_0.x,r1_0.y from Rectangle r1_0 where r1_0.color=?
19:11:37,171 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [1] as [VARCHAR] - [#ffffff]
19:11:37,179 INFO  [com.thorben.janssen.model.ColorConverter] - Convert #ffffff to java.awt.Color[r=255,g=255,b=255]
19:11:37,181 INFO  [com.thorben.janssen.model.ColorConverter] - Convert java.awt.Color[r=255,g=255,b=255] to #ffffff
19:11:37,181 INFO  [com.thorben.janssen.model.ColorConverter] - Convert #ffffff to java.awt.Color[r=255,g=255,b=255]
19:11:37,184 DEBUG [org.hibernate.stat.internal.StatisticsImpl] - HHH000117: HQL: SELECT r FROM Rectangle r WHERE r.color = :color, time: 39ms, rows: 1
19:11:37,192 DEBUG [org.hibernate.SQL] - update Rectangle set color=?, x=?, y=? where id=?
19:11:37,193 INFO  [com.thorben.janssen.model.ColorConverter] - Convert java.awt.Color[r=0,g=0,b=0] to #ff0000
19:11:37,193 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [1] as [VARCHAR] - [#ff0000]
19:11:37,193 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [2] as [INTEGER] - [10]
19:11:37,193 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [3] as [INTEGER] - [20]
19:11:37,193 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [4] as [INTEGER] - [1]
19:11:37,196 INFO  [com.thorben.janssen.model.ColorConverter] - Convert java.awt.Color[r=0,g=0,b=0] to #ff0000
19:11:37,196 INFO  [com.thorben.janssen.model.ColorConverter] - Convert #ff0000 to java.awt.Color[r=255,g=0,b=0]
19:11:37,203 INFO  [org.hibernate.engine.internal.StatisticalLoggingSessionEventListener] - Session Metrics {
    31200 nanoseconds spent acquiring 1 JDBC connections;
    26100 nanoseconds spent releasing 1 JDBC connections;
    191100 nanoseconds spent preparing 2 JDBC statements;
    4859600 nanoseconds spent executing 2 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    13747100 nanoseconds spent executing 1 flushes (flushing a total of 1 entities and 0 collections);
    770600 nanoseconds spent executing 1 partial-flushes (flushing a total of 0 entities and 0 collections)
}

结论

AttributeConverter 提供了一种简单和可移植的方式来定义自定义类型映射。你可以把它用于所有你想映射到1个数据库列的基本属性。在这篇文章中,我用它来将一个java.awt.Color类型的实体属性持久化为一个6位数的十六进制代码。但是,这当然不是你能实现的唯一一种映射方式。我在其他文章中用它来改进Hibernate的标准枚举映射,并在不支持 LocalDate和LocalDateTime类型的旧版Hibernate中映射LocalDate和LocalDateTime

正如你在这篇文章中看到的,实现AttributeConverter很简单。你只需要实现AttributeConverter 接口及其2个转换方法,并用*@Converter注解来注释该类。如果你将该注解的autoApply* 属性设置为*"true*",你的持久化提供者将对所有支持类型的实体属性使用该转换器。如果你不设置该属性或将其设置为false,你需要用*@Convert*注解每个你想使用转换器的实体属性并引用你的转换器实现。

推荐阅读