【问题标题】:Preventing NaN from being persisted by Hibernate防止 NaN 被 Hibernate 持久化
【发布时间】:2010-12-20 22:12:27
【问题描述】:

我使用 Hibernate 作为我的 JPA 提供程序,它连接到 Progress 数据库。当 NaN 值被持久化时,它会导致很多问题——它会阻止在某些情况下读取该行。有没有办法连接到标准的双类型持久性以将 NaN(可能还有 + 和 - 无穷大)转换为不同的值?丢失NaN或无穷大信息都没有关系,我只想要一个可读的行!

我知道我可以这样做:

@Column(name = "doubleColumn")
public double getDoubleColumn() {
    return PersistedDouble.convert(doubleColumn);
}

但我担心维护,因为必须为映射到数据库的任何双打手动添加。

【问题讨论】:

  • 您是否考虑过使用 Hibernate Validator 的 @NotNull 注释? (如果您只想要纯 JPA,这可能不是一个选项)

标签: java hibernate persistence orm progress


【解决方案1】:

Following this discussion,我有一种感觉,hibernate 没有提供将 NaN 转换为其他东西的方法。我认为,您必须更早地阻止 NaN 值,甚至在将它们写入 bean 成员变量之前(例如向设置器添加保护/转换代码)。

编辑

我担心,最好的不愉快的解决方案是使用保护代码,更糟糕的是,在表格上增加一列来标记值是否为数字。什么肯定会使查询和插入操作复杂化。但是您需要在数据库中使用 NaN,并且您无法与 jdbc 驱动程序/数据库对抗以使其正常运行(并接受 NaN 作为 NUMBER 字段的有效输入)。

【讨论】:

  • 谢谢你 - 我希望讨论中的参与者可能错过了一些东西。守卫(或就此而言,UserType)解决方案遭受与我在问题中描述的相同的维护问题。另外在这种情况下,NaN 实际上是正确的结果,因此在对象级别使其非法(即使它在映射时丢失)感觉不合适。
  • 这很糟糕 - 因为它显然是数据库和 jdbc 驱动程序在处理 NaN 而不是休眠时有问题......而且这个 NaN 问题似乎阻碍了可移植(独立于数据库)的解决方案......
  • 是的,这绝对是一个 JDBC 问题——我希望 Hibernate 能够为我解决这些错误!无论如何,我希望我也能接受你的回答,但 SO 只会让我选择一个。
【解决方案2】:

我对此的第一印象是寻找 Hibernate 坚持 double 的类型。所以你可以重构DoubleType中的set(...)方法。这意味着尽管在 package-info 中使用 @org.hibernate.annotations.TypeDef 定义“myDouble”之后,您需要用 @org.hibernate.annotations.type(type="myDouble") 注释每个 Double 类型——我认为您希望避免所有这些以进行维护(除了您必须进入 Hibernate 的核心)。

【讨论】:

  • 这似乎是最好的解决方案 - 感谢您的帮助。 137 个 @Type 注释之后,它就可以工作了!
【解决方案3】:

你可以修改 hibernate 本身。您所要做的就是更改 DoubleType 类。

当然,随着 hibernate 的发展,您必须维护该补丁,但考虑到它位于一个相当稳定的单个类中,这可能比为域模型中的每个双精度指定一个 UserType 更容易。

【讨论】:

  • 这绝对是正确的 - 不幸的是,SO 只会让我接受一个答案。我考虑过,但正如你提到的,当 Hibernate 更新时它有点痛苦。感谢您的帮助。
【解决方案4】:

我最终使用了 UserType 解决方案,但通过单元测试解决了维护问题。类型类如下:

public class ParsedDoubleType extends DoubleType {
    private static final long serialVersionUID = 1L;

    @Override
    public void set(PreparedStatement st, Object value, int index) throws SQLException {
        Double doubleValue = (Double) value;
        if (doubleValue.isInfinite() || doubleValue.isNaN()) {
            Logger.getLogger(ParsedDoubleType.class).warn("Attempted to send a NaN or infinity value to the database " 
                + "- this is not supported.\nStatement=" + st + " valueIndex=" + index);
            doubleValue = Double.valueOf(0);
        }
        super.set(st, doubleValue, index);
    }
}

单元测试大致如下(为简洁起见,删除了一些细节):

Ejb3Configuration hibernateConfig = new Ejb3Configuration().configure("InMemoryDatabasePersistenceUnit", null);
for (Iterator<?> iterator = hibernateConfig.getClassMappings(); iterator.hasNext();) {
    PersistentClass clazz = (PersistentClass) iterator.next();
    Iterator<?> propertyIterator = clazz.getPropertyIterator();
    while (propertyIterator.hasNext()) {
        if (property.getType().equals(Hibernate.DOUBLE)) {
            Assert.fail("Raw double type found. Annotate with @Type(type = \"package.ParsedDoubleType\")\n" 
                + "Class " + clazz + " property " + property);
        }
    }
}

【讨论】:

    【解决方案5】:

    我遇到了完全相同的问题,在这些解决方案的指导下,我还准备了一个自定义类型类,扩展了 DoubleType。在该类中,我在 set 函数中将 NaN 值转换为 null,对于 get 函数反之亦然,因为 null 对于我的数据库列是可以的。我还将 NaN 可能列的映射更改为自定义类型类。该解决方案非常适合休眠 3.3.2。

    不幸的是,在将 Hibernate 升级到 3.6.10 后,它停止了工作。为了让它再次工作,我将自定义类型从扩展 DoubleType 替换为实现 UserType。

    重要的数据类型函数实现应该如下:

    private int[] types = { Types.DOUBLE };
    
    public int[] sqlTypes()
    {
        return types;
    }
    
    @SuppressWarnings("rawtypes")
    public Class returnedClass()
    {
        return Double.class;
    }
    

    这里是get和set函数:

    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException
    {
        Double value = rs.getDouble(names[0]);
        if (rs.wasNull())
            return Double.NaN;
        else
            return value;
    }
    
    public void nullSafeSet(PreparedStatement ps, Object value, int index) throws HibernateException, SQLException
    {
        Double dbl = (Double) value;
        if ((dbl == null) || (Double.isNaN(dbl)))
            ps.setNull(index, Types.DOUBLE);
        else
            ps.setDouble(index, dbl);
    }
    

    【讨论】:

      【解决方案6】:

      很抱歉,但从您的示例和您的问题来看,您实际上在理解 java 持久性方面存在问题。数据库实体通过 getter 和 setter 进行自我管理——它们可以进行您希望它们进行的任何验证。如果您真的在没有它们的情况下设置属性,那么您将缺少面向对象开发和持久性的核心概念——尤其是托管实体。 我认为你需要重新设计你的项目,遇到这样的问题是基本设计缺陷的明确迹象......在这里给出一些建议 - 这就是解决方案:

      @Column(name="doubleColumn"}
      private Double doubleColumn = Double.NaN  //yes, this is intentional. Verily.
      
      public void setDouble(Double d)
      {
          if(d.isNan || d.isInfinite()
          {
             //do something nice here
          }
          else
             this.doubleColumn = d;
      }
      public Double getDouble()
      {
         return !this.doubleColumn.isNaN() && !this.doubleColumn.isInfinite() ? this.doubleColumn : new Double();
      }
      

      ....就这么简单。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-12
        相关资源
        最近更新 更多