如何在Hibernate-Mapping XML(HBM 4.3)中使用JPA AttributeConverter?
这个问题我之前也碰到过,Hibernate 4.3.x对JPA AttributeConverter的hbm.xml支持确实有局限性——你直接在<property>的type属性里指定Converter类是行不通的,因为Hibernate的type字段需要的是Hibernate自身的Type接口实现(比如UserType或BasicType),而不是JPA规范的AttributeConverter类。
结合你的场景(Avro生成的实体无法加注解),下面是可行的解决步骤:
1. 用自定义UserType封装AttributeConverter逻辑
创建一个实现Hibernate UserType接口的类,把你的DateToLongConverter的转换逻辑包裹进去:
import org.hibernate.usertype.UserType; import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.Date; import org.hibernate.engine.spi.SessionImplementor; import dao.converter.DateToLongConverter; public class DateToLongUserType implements UserType { // 实例化你的JPA转换器 private final DateToLongConverter converter = new DateToLongConverter(); @Override public int[] sqlTypes() { // 对应数据库中的BIGINT类型 return new int[]{Types.BIGINT}; } @Override public Class<?> returnedClass() { // 实体类中的属性类型 return Date.class; } @Override public boolean equals(Object x, Object y) { if (x == y) return true; if (x == null || y == null) return false; return x.equals(y); } @Override public int hashCode(Object x) { return x.hashCode(); } @Override public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws SQLException { // 从数据库读取Long,转换为Date Long dbValue = rs.getLong(names[0]); return rs.wasNull() ? null : converter.convertToEntityAttribute(dbValue); } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws SQLException { // 把Date转换为Long写入数据库 if (value == null) { st.setNull(index, Types.BIGINT); return; } Long dbValue = converter.convertToDatabaseColumn((Date) value); st.setLong(index, dbValue); } @Override public Object deepCopy(Object value) { // Date是不可变类型,直接返回即可 return value; } @Override public boolean isMutable() { return false; } @Override public Serializable disassemble(Object value) { return (Serializable) value; } @Override public Object assemble(Serializable cached, Object owner) { return cached; } @Override public Object replace(Object original, Object target, Object owner) { return original; } }
2. 修改hbm.xml映射
在<property>标签中,将type属性指定为你刚才创建的UserType的全类名,而不是原来的AttributeConverter类:
<property name="aColumn" column="A_COLUMN" type="dao.usertype.DateToLongUserType"/>
3. 移除不必要的配置
因为我们已经通过UserType封装了转换逻辑,你可以移除persistence.xml中对DateToLongConverter的注册(保留也不会有冲突,但没必要)。
为什么原来的方法会报错?
Hibernate 4.3.x虽然支持JPA 2.1的AttributeConverter,但它的hbm.xml解析器并没有实现直接引用AttributeConverter类的逻辑——type字段只能识别Hibernate原生的Type实现,或者你自定义的UserType/BasicType。而AttributeConverter通常是通过实体类上的@Convert注解来关联的,但你的实体是Avro生成的无法添加注解,所以必须用UserType来做中间层。
如果未来有机会升级到Hibernate 5.x+,你会发现它对hbm.xml的Converter支持更友好,可以直接通过<property>的converter属性指定Converter类,但在4.3.x版本中,上述方法是最稳妥的解决方案。
内容的提问来源于stack exchange,提问作者Deandre Bauswell

