My Domain对象有几个Joda-Time DateTime
字段。当我使用SimpleJdbcTemplate读取数据库值时:
患者患者= jdbc.queryForObject(sql,new BeanPropertyRowMapper(Patient.class),patientId);
它失败了,令人惊讶的是,没有记录任何错误。我想这是因为对timestamp
的{{1}}解析不适用于Jdbc。
如果可以继承并覆盖DateTime
并指示将所有BeanPropertyRowMapper
和java.sql.Timestamp
转换为java.sql.Date
,那就太棒了,可以节省大量额外的代码
有什么建议吗?
答案 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;
}
}