Corda 3.0是否支持持久化java.time系列数据类型?
关于Corda 3.0中
java.time.Year持久化的问题解答 没错,Corda 3.0(搭配其内置的Hibernate 5.2.6和JPA 2.1)确实不默认支持java.time.Year类型的持久化。原因很直接:
- JPA 2.1规范本身并未包含对
Year这个类型的映射定义 - Hibernate 5.2.x虽然对
java.time下的常用类型(比如LocalDate、LocalDateTime)提供了内置支持,但Year不在默认支持的列表中,所以当FinalityFlow提交状态时,Hibernate无法找到对应的类型映射,就会抛出错误。
下面给你几个可行的解决方案:
方案1:自定义Hibernate用户类型
你可以实现Hibernate的UserType接口,手动定义Year与数据库字段的转换逻辑,比如将Year转为数据库中的INTEGER类型存储。
自定义类型示例代码
public class YearUserType implements UserType { @Override public int[] sqlTypes() { return new int[]{Types.INTEGER}; } @Override public Class returnedClass() { return Year.class; } @Override public boolean equals(Object x, Object y) throws HibernateException { if (x == y) return true; if (x == null || y == null) return false; return x.equals(y); } @Override public int hashCode(Object x) throws HibernateException { return x.hashCode(); } @Override public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { Integer yearValue = rs.getInt(names[0]); return rs.wasNull() ? null : Year.of(yearValue); } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { if (value == null) { st.setNull(index, Types.INTEGER); } else { Year year = (Year) value; st.setInt(index, year.getValue()); } } // 其余方法默认实现 @Override public Object deepCopy(Object value) throws HibernateException { return value; } @Override public boolean isMutable() { return false; } @Override public Serializable disassemble(Object value) throws HibernateException { return (Serializable) value; } @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } }
在实体类中使用自定义类型
修改你的实体类,给issuedYear字段加上@Type注解指定自定义类型:
@Entity @Table(name = "custom_states") class CustomEntity( @Column(name = "issued_date") var issuedDate: LocalDate? = null, @Column(name = "issued_year") @Type(type = "com.your.package.YearUserType") // 替换成你的自定义类包路径 var issuedYear: Year? = null )
方案2:改用Integer存储年份(最简单的替代方案)
如果不想自定义类型,可以直接用Integer来存储年份值,在业务逻辑层再转换为Year类型:
修改后的实体类
@Entity @Table(name = "custom_states") class CustomEntity( @Column(name = "issued_date") var issuedDate: LocalDate? = null, @Column(name = "issued_year") var issuedYear: Int? = null // 用Int存储年份 ) { // 可选:添加辅助方法,方便获取Year类型 fun getIssuedYearAsYear(): Year? { return issuedYear?.let { Year.of(it) } } }
在业务代码中,设置年份时调用year.getValue()获取整数值;读取时用辅助方法转回Year即可。
方案3:用LocalDate间接存储年份
另一种简单的方式是用LocalDate存储年份的第一天(比如Year.of(2024).atDay(1)),利用Hibernate对LocalDate的内置支持,读取时再提取年份:
实体类示例
@Entity @Table(name = "custom_states") class CustomEntity( @Column(name = "issued_date") var issuedDate: LocalDate? = null, @Column(name = "issued_year_date") var issuedYearDate: LocalDate? = null // 存储年份的第一天 ) { fun getIssuedYear(): Year? { return issuedYearDate?.let { Year.from(it) } } fun setIssuedYear(year: Year?) { issuedYearDate = year?.atDay(1) } }
这种方式不需要额外的自定义类型,缺点是数据库字段会存储完整的日期,但对于只需要年份的场景来说也能满足需求。
内容的提问来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

