防止NaN被Hibernate持久化

时间:2009-11-27 09:14:08

标签: java hibernate persistence orm progress

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

我知道我可以这样做:

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

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

6 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:2)

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

当然,你必须在hibernate发展时维护该补丁,但考虑到它在一个相当稳定的类中,这可能比为你的域模型中的每个double指定一个UserType更容易。

答案 2 :(得分:1)

Following this discussion,我感觉,hibernate没有提供将NaN转换为其他内容的方法。我认为,你必须提前阻止NaN值,甚至在它们被写入bean成员变量之前(比如将保护/转换代码添加到setter)。

修改

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

答案 3 :(得分:1)

我最终使用了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);
        }
    }
}

答案 4 :(得分:1)

我有完全相同的问题,并且在这些解决方案的指导下,我还准备了一个扩展DoubleType的自定义类型类。在该类中,我在set函数中将NaN值转换为null,反之亦然,因为对于我的数据库列,null是可以的。我还将NaN可能列的映射更改为自定义类型类。该解决方案非常适用于hibernate 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);
}

答案 5 :(得分:-1)

抱歉,但从您的示例和您的问题判断,您实际上在理解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();
}

....这很容易。