代码之家  ›  专栏  ›  技术社区  ›  Russ Hayward

防止NAN持续休眠

  •  2
  • Russ Hayward  · 技术社区  · 15 年前

    我使用Hibernate作为JPA提供者,它连接到一个进度数据库。当一个NAN值被持久化时,它会引起很多问题——它会防止在某些情况下读取行。有没有一种方法可以连接到标准的双类型持久性来将NaN(可能是+和-无穷大)转换为不同的值?不管NAN或无穷大的信息是否丢失,我只需要一个可读的行!

    我知道我可以这样做:

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

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

    6 回复  |  直到 11 年前
        1
  •  3
  •   non sequitor    15 年前

    我对此的第一印象是寻找冬眠持续的类型。 double AS。所以您可以重构 set(...) 方法在 DoubleType . 这意味着,尽管你需要注释 Double 类型与 @org.hibernate.annotations.type(type="myDouble") 定义“mydouble”后,使用 @org.hibernate.annotations.TypeDef 在软件包信息中——我认为为了维护,您需要避免所有这些(除此之外,您必须进入休眠的中心)。

        2
  •  2
  •   meriton    15 年前

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

    当然,随着Hibernate的发展,您必须维护这个补丁,但是考虑到它在一个单一的、相当稳定的类中,这可能比为域模型中的每一个double指定一个usertype要容易。

        3
  •  1
  •   Andreas Dolk    15 年前

    Following this discussion 我有一种感觉,冬眠并没有提供一种方法将南转化成别的东西。我认为,您必须在将NAN值写入bean成员变量(比如向setter添加保护/转换代码)之前,提前阻止它们。

    编辑

    我担心,最好的不愉快的解决方案是使用保护代码,更糟糕的是,在表上增加一列来标记,不管值是否是数字。这无疑会使查询和插入操作复杂化。但是您需要在数据库中使用NaN,并且您不能与JDBC驱动程序/数据库作斗争,使其正常工作(并且接受NaN作为数字字段的有效输入)。

        4
  •  1
  •   Russ Hayward    15 年前

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

    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
  •  1
  •   muammer    11 年前

    我有完全相同的问题,在这些解决方案的指导下,我还准备了一个扩展doubletype的自定义类型类。在该类中,我在set函数中将NaN值转换为空值,而在get函数中将NaN值转换为空值,因为我的数据库列可以使用空值。我还更改了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);
    }
    
        6
  •  -1
  •   hduzqibchxasbxhasdaf    11 年前

    抱歉,但是从你的例子和你的问题判断你在理解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();
    }
    

    …这很容易。