2009-11-27 367 views
2

我使用Hibernate作爲我的JPA提供程序,它連接到Progress數據庫。當一個NaN值持續存在時,會造成很多問題 - 它會阻止在某些情況下讀取該行。有沒有辦法掛鉤到標準的雙類型持久性轉換NaN(可能+和 - 無窮大)到一個不同的值? NaN或無限信息是否丟失並不重要,我只想要一個可讀的行!防止NaN被Hibernate持久化

我知道我可以做這樣的事情:

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

但我擔心的是維護,將必須手動添加映射到數據庫的任何雙打。

+0

你看過使用Hibernate Validator的@NotNull註釋嗎? (如果你只需要純JPA,這可能不是一個選項) – Tim 2009-11-28 11:36:26

+0

@NotNull是否檢查NaN? – 2009-11-28 11:39:41

回答

3

我對此的第一印象是尋找Hibernate持續存在的類型double。所以你可以在DoubleType中重構set(...)方法。這意味着雖然你需要註釋每個Double類型與@org.hibernate.annotations.type(type="myDouble")之後,你已經使用包裝信息中的@org.hibernate.annotations.TypeDef定義「myDouble」 - 我想你想避免所有這些維護(除此之外,你將不得不進入Hibernate的心臟)。

+0

這似乎是最好的解決方案 - 感謝您的幫助。 137 @Type註釋後面,它的工作原理! – 2009-11-28 11:37:05

1

Following this discussion,我有這樣的感覺,即hibernate並沒有提供將NaN轉換成別的方法。我想,即使在寫入到bean成員變量之前(例如添加守護/轉換代碼到setters),您也必須提前阻止NaN值。

編輯

我擔心,最好不愉快的解決方案是使用後衛代碼,這是更糟的是,在表中的附加列,以國旗,值是否是一個數字或沒有。這肯定會使查詢和插入操作複雜化。但是你需要在數據庫中使用NaN,並且不能與jdbc驅動程序/數據庫進行對抗(並接受NaN作爲NUMBER字段的有效輸入)。

+0

非常感謝 - 我希望可能有些討論的參與者錯過了。警衛(或者UserType)解決方案遭受與我在問題中描述的相同的維護問題。此外,在這種情況下,NaN實際上是正確的結果,因此在對象級別將其設置爲非法(即使它在映射時會丟失)感覺不合適。 – 2009-11-27 10:34:17

+0

這很糟糕 - 因爲它顯然是數據庫和jdbc驅動程序在處理NaN時遇到問題,而不是休眠......並且這個NaN問題似乎阻止了可移植(數據庫獨立)解決方案...... – 2009-11-27 10:44:39

+0

是的,這絕對是一個JDBC問題 - 我希望Hibernate能夠繞過我的錯誤!無論如何,我希望我也能接受你的回答,但是SO只會讓我選擇一個。 – 2009-11-28 11:35:05

1

你可以修改休眠本身。你所要做的就是改變DoubleType類。當然,隨着hibernate的發展,你將不得不維護那個補丁,但考慮到它是在一個單一的,相當穩定的類中,這可能比爲你的域模型中的每個double指定一個UserType更容易。

+0

這絕對是正確的 - 不幸的是,SO只會允許我接受一個答案。我認爲它,但正如你所提到的那樣,當Hibernate更新時會有點痛苦。謝謝你的幫助。 – 2009-11-28 11:32:47

1

我在最後使用了UserType解決方案,但通過單元測試解決了維護問題。 Type類如下:

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); 
     } 
    } 
} 
1

我有完全相同的問題,以及這些解決方案的指導下,我還準備了一個擴展DoubleType的自定義類型類。在該類中,我將NaN值轉換爲null函數,對於get函數將NaN值轉換爲null,因爲null對於我的數據庫列是OK的。我還將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); 
} 
-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(); 
} 

....它是簡單。