2011-10-11 119 views
10

我的域對象有幾個Joda-Time DateTime字段。當我在讀使用了SimpleJdbcTemplate數據庫值:覆蓋BeanPropertyRowMapper以支持JodaTime DateTime

患者的患者= jdbc.queryForObject(SQL,新 BeanPropertyRowMapper(Patient.class),patientId);

它只是失敗,令人驚訝的是,沒有記錄錯誤。我想這是因爲timestamp解析爲DateTime不適用於Jdbc。

如果可以繼承和覆蓋BeanPropertyRowMapper並指示所有java.sql.Timestampjava.sql.Date轉換爲DateTime,這將是偉大的,可以節省大量的額外的代碼。

有什麼建議嗎?

回答

21

做正確的事情是繼承BeanPropertyRowMapper,覆蓋initBeanWrapper(BeanWrapper)和註冊一個自定義屬性編輯器:

public class JodaDateTimeEditor extends PropertyEditorSupport { 
    @Override 
    public void setAsText(final String text) throws IllegalArgumentException { 
     setValue(new DateTime(text)); // date time in ISO8601 format 
             // (yyyy-MM-ddTHH:mm:ss.SSSZZ) 
    } 
    @Override 
    public void setValue(final Object value) { 
     super.setValue(value == null || value instanceof DateTime ? value 
             : new DateTime(value)); 
    } 
    @Override 
    public DateTime getValue() { 
     return (DateTime) super.getValue(); 
    } 
    @Override 
    public String getAsText() { 
     return getValue().toString(); // date time in ISO8601 format 
             // (yyyy-MM-ddTHH:mm:ss.SSSZZ) 
    } 
} 
public class JodaTimeSavvyBeanPropertyRowMapper<T> 
        extends BeanPropertyRowMapper<T> { 
    @Override 
    protected void initBeanWrapper(BeanWrapper bw) { 
     bw.registerCustomEditor(DateTime.class, new JodaDateTimeEditor()); 
    } 
} 
+0

這正是我要找的。但是,它並不真正爲我工作。執行在DateTime映射發生時停止,但不記錄錯誤:'DEBUG:com.keype.hawk.system.jdbc.support.HawkBeanPropertyRowMapper [mapRow]:將'date_added'列映射到class org類型的屬性'dateAdded'。 joda.time.DateTime' –

+0

最後管理到問題區域。 setValue()被一個TimeStamp對象作爲參數調用,但它實際上是一個JodaTime每個我的記錄器:'Un支持的對象2011-10-12 00:00:00.0。對象類型:java.sql.Timestamp。期望日期時間' –

+0

@firdousamir好的,我現在更改了代碼。試試這個版本。 –

2

看着BeanPropertyRowMapper實現,它設置的字段的方法是:

Object value = getColumnValue(rs, index, pd); 

if (logger.isDebugEnabled() && rowNumber == 0) { 
    logger.debug("Mapping column '" + column + "' to property '" + 
    pd.getName() + "' of type " + pd.getPropertyType()); 
} 
try { 
    bw.setPropertyValue(pd.getName(), value); 
} 

其中getColumnValue(rs, index, pd);委託給JdbcUtils.getResultSetValue

,在getColumnValuepd字段是實際的「p roperty d escriptor 「,即JdbcUtils使用(pd.getPropertyType())作爲類型要映射到的字段。

如果你看一下JdbcUtils代碼getResultSetValue方法,你會看到,它只是從一個if語句去另一個,匹配pd.getPropertyType()所有標準的類型。如果它沒有找到一個,因爲DateTime是不是一個「標準」型,它依賴於rs.getObject()

} else { 
// Some unknown type desired -> rely on getObject. 

那麼如果這個對象是將其轉換爲Timestamp一個SQL日期,並返回到設置到您的域的DateTime字段=>失敗。

因此,似乎沒有被注入一個Date/TimestampDateTime轉換器轉換成BeanPropertyRowMapper一個直接的方式。所以它會更乾淨(更高性能)來實現你自己的RowMapper。

如果您想在控制檯中看到映射錯誤,請將日誌記錄級別設置爲org.springframework.jdbc以「調試」或更好的「跟蹤」來查看發生了什麼。

有一兩件事你可以試試,我沒有測試過,是延長一個BeanPropertyRowMapper和覆蓋DateTime類型的屬性:

/** 
* Initialize the given BeanWrapper to be used for row mapping. 
* To be called for each row. 
* <p>The default implementation is empty. Can be overridden in subclasses. 
* @param bw the BeanWrapper to initialize 
*/ 
protected void initBeanWrapper(BeanWrapper bw) {} 
+0

感謝tolitius。這就是我所做的。我也找不出方法。自定義的行問題。 –

1

@Sean Patrick Floyd的答案是完美的,直到你沒有很多很多自定義類型。

這一種普遍性的,可配置的基礎上使用擴展:

public class CustomFieldTypeSupportBeanPropertyRowMapper<T> extends BeanPropertyRowMapper<T> { 
    private Map<Class<?>, Handler> customTypeMappers = new HashMap<Class<?>, Handler>(); 

    public CustomFieldTypeSupportBeanPropertyRowMapper() { 
    super(); 
    } 

    public CustomFieldTypeSupportBeanPropertyRowMapper(Class<T> mappedClass, boolean checkFullyPopulated) { 
    super(mappedClass, checkFullyPopulated); 
    } 

    public CustomFieldTypeSupportBeanPropertyRowMapper(Class<T> mappedClass) { 
    super(mappedClass); 
    } 

    public CustomFieldTypeSupportBeanPropertyRowMapper(Class<T> mappedClass, Map<Class<?>, Handler> customTypeMappers) { 
    super(mappedClass); 
    this.customTypeMappers = customTypeMappers; 
    } 

    @Override 
    protected Object getColumnValue(ResultSet rs, int index, PropertyDescriptor pd) throws SQLException { 
    final Class<?> current = pd.getPropertyType(); 
    if (customTypeMappers.containsKey(current)) { 
     return customTypeMappers.get(current).f(rs, index, pd); 
    } 
    return super.getColumnValue(rs, index, pd); 
    } 

    public void addTypeHandler(Class<?> class1, Handler handler2) { 
    customTypeMappers.put(class1, handler2); 
    } 

    public static interface Handler { 
    public Object f(ResultSet rs, int index, PropertyDescriptor pd) throws SQLException; 
    } 
}