Java 防止NaN被Hibernate持久化

Java 防止NaN被Hibernate持久化,java,hibernate,persistence,orm,progress,Java,Hibernate,Persistence,Orm,Progress,我使用Hibernate作为我的JPA提供者,它连接到一个进度数据库。当一个NaN值被持久化时,它会导致很多问题——在某些情况下,它会阻止读取该行。有没有办法连接到标准的双类型持久性来将NaN(可能还有+和-无穷大)转换为不同的值?如果NaN或infinity信息丢失,这并不重要,我只想要一个可读的行 我知道我可以这样做: @Column(name = "doubleColumn") public double getDoubleColumn() { return PersistedDo

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

我知道我可以这样做:

@Column(name = "doubleColumn")
public double getDoubleColumn() {
    return PersistedDouble.convert(doubleColumn);
}
但我担心的是维护,因为对于映射到数据库的任何double,都必须手动添加维护。

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

编辑


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

我的第一印象是寻找Hibernate持久化的类型
double
as。因此,您可以在中重构
set(…)
方法。这意味着,在您使用包信息中的
@org.hibernate.annotations.type(type=“myDouble”)
定义了“myDouble”之后,您需要使用
@org.hibernate.annotations.TypeDef
对每个
双精度
类型进行注释——我认为为了维护,您希望避免这一切(除此之外,您还必须进入Hibernate的核心)。

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


当然,随着hibernate的发展,您必须维护该修补程序,但考虑到它在一个单一的、相当稳定的类中,这可能比为域模型中的每一个double指定一个用户类型更容易。

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

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);
    }
}
private int[] types = { Types.DOUBLE };

public int[] sqlTypes()
{
    return types;
}

@SuppressWarnings("rawtypes")
public Class returnedClass()
{
    return Double.class;
}
单元测试大致如下(为了简洁起见,删除了一些细节):

Ejb3Configuration hibernateConfig=new Ejb3Configuration().configure(“InMemoryDatabasePersistenceUnit”,null);
for(Iterator Iterator=hibernateConfig.getClassMappings();Iterator.hasNext();){
PersistentClass clazz=(PersistentClass)迭代器.next();
迭代器propertyIterator=clazz.getPropertyIterator();
while(propertyIterator.hasNext()){
if(property.getType().equals(Hibernate.DOUBLE)){
Assert.fail(“找到原始双精度类型。用@type(type=\“package.ParsedDoubleType\”注释)\n”
+“类别”+类别+“财产”+财产);
}
}
}

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

不幸的是,在将Hibernate升级到3.6.10之后,它停止了工作。为了使它再次工作,我将自定义类型从扩展DoubleType替换为实现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);
    }
}
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);
}

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

@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();
}

…这很简单。

谢谢你-我希望讨论中的参与者可能遗漏了一些东西。警卫(或就此而言的用户类型)解决方案遇到了与我在问题中描述的相同的维护问题。此外,在这种情况下,NaN实际上是正确的结果,因此在对象级别将其设置为非法是不合适的(即使在映射时丢失).这很糟糕-因为显然是数据库和jdbc驱动程序在处理NaN时遇到问题,而不是hibernate…而这个NaN问题似乎阻止了可移植(独立于数据库)的实现解决方案…是的,这绝对是一个JDBC问题-我希望Hibernate能够为我绕过这些错误!无论如何,我希望我也能接受你的答案,但我只允许我选择一个。这绝对正确-不幸的是,我只允许我接受一个答案。我考虑过了,但正如你提到的,这有点痛苦当Hibernate更新时。谢谢你的帮助。你有没有考虑过使用Hibernate Validator的@NotNull注释?(如果你只想要纯JPA,这可能不是一个选项)这似乎是最好的解决方案-谢谢你的帮助。137@稍后键入注释,它就可以工作了!