重写BeanPropertyRowMapper以支持JodaTime DateTime

My Domain对象有几个Joda-Time DateTime字段。 当我使用SimpleJdbcTemplate读取数据库值时:

患者患者= jdbc.queryForObject(sql,new BeanPropertyRowMapper(Patient.class),patientId);

它只是失败,令人惊讶的是,没有记录任何错误。 我想这是因为解析到DateTimetimestamp不适用于Jdbc。

如果可以inheritance和覆盖BeanPropertyRowMapper并指示将所有java.sql.Timestampjava.sql.Date转换为DateTime ,那么它会很棒并且可以节省大量额外代码。

任何建议?

正确的做法是initBeanWrapper(BeanWrapper) 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 extends BeanPropertyRowMapper { @Override protected void initBeanWrapper(BeanWrapper bw) { bw.registerCustomEditor(DateTime.class, new JodaDateTimeEditor()); } } 

查看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

getColumnValue中的pd字段是实际的“ p roperty d escriptor”,它在JdbcUtils用作( pd.getPropertyType() )作为要映射到的字段的类型

如果你看一下getResultSetValue方法的JdbcUtils代码,你会看到它只是从一个if语句转到另一个,以匹配pd.getPropertyType()到所有标准类型。 当它找不到时,由于DateTime不是“标准”类型,它依赖于rs.getObject()

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

然后,如果此对象是SQL Date,则将其转换为Timestamp ,并返回以设置为domain =>的DateTime字段。

因此,似乎没有一种向DateTime转换器注入Date / TimestampBeanPropertyRowMapper 。 因此,实现自己的RowMapper会更干净(也更高效)。

如果您希望在控制台中看到映射错误,请将org.springframework.jdbc的日志记录级别设置为“debug”或更好地“跟踪”以确切了解发生的情况。

您可以尝试的一件事,我还没有测试,是扩展BeanPropertyRowMapper并覆盖DateTime类型的属性:

 /** * Initialize the given BeanWrapper to be used for row mapping. * To be called for each row. * 

The default implementation is empty. Can be overridden in subclasses. * @param bw the BeanWrapper to initialize */ protected void initBeanWrapper(BeanWrapper bw) {}

@Sean Patrick Floyd的答案是完美的,直到你没有很多自定义类型。

这是一个基于使用扩展的通用可配置:

 public class CustomFieldTypeSupportBeanPropertyRowMapper extends BeanPropertyRowMapper { private Map, Handler> customTypeMappers = new HashMap, Handler>(); public CustomFieldTypeSupportBeanPropertyRowMapper() { super(); } public CustomFieldTypeSupportBeanPropertyRowMapper(Class mappedClass, boolean checkFullyPopulated) { super(mappedClass, checkFullyPopulated); } public CustomFieldTypeSupportBeanPropertyRowMapper(Class mappedClass) { super(mappedClass); } public CustomFieldTypeSupportBeanPropertyRowMapper(Class mappedClass, Map, 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; } }