重写BeanPropertyRowMapper以支持JodaTime DateTime

时间:2011-10-11 18:14:07

标签: java spring jodatime spring-jdbc

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

  

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

它失败了,令人惊讶的是,没有记录任何错误。我想这是因为对timestamp的{​​{1}}解析不适用于Jdbc。

如果可以继承并覆盖DateTime并指示将所有BeanPropertyRowMapperjava.sql.Timestamp转换为java.sql.Date,那就太棒了,可以节省大量额外的代码

有什么建议吗?

3 个答案:

答案 0 :(得分:22)

正确的做法是继承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());
    }
}

答案 1 :(得分: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

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

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

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

然后,如果此对象是SQL日期,则将其转换为Timestamp,并返回以设置为您的域的DateTime字段=&gt;失败的地方。

因此,似乎没有一种直接的方法将Date / Timestamp转换为DateTime转换器到BeanPropertyRowMapper。因此,实现自己的RowMapper会更干净(也更高效)。

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

您可以尝试的一件事,我尚未测试,是扩展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) {}

答案 2 :(得分: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;
  }
}