Переопределение BeanPropertyRowMapper для поддержки JodaTime DateTime
Объект My Domain имеет пару полей Joda-Time DateTime
. Когда я читаю значения базы данных с помощью SimpleJdbcTemplate:
Пациент пациента = jdbc.queryForObject(sql, new BeanPropertyRowMapper (Patient.class), patientId);
Он просто терпит неудачу и неожиданно, ошибки не были зарегистрированы. Я думаю, это из-за синтаксического анализа timestamp
на DateTime
не работает с Jdbc.
Если возможно наследовать и переопределить BeanPropertyRowMapper
и указать, чтобы преобразовать все java.sql.Timestamp
и java.sql.Date
в DateTime
, это было бы здорово и могло бы сэкономить много дополнительного кода.
Любые советы?
Ответы
Ответ 1
Правильная вещь - подкласс 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());
}
}
Ответ 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 Date, он преобразует его в Timestamp
и возвращается для установки в поле DateTime
вашего домена = > , где он терпит неудачу.
Следовательно, кажется, что нет прямого способа вставить преобразователь Date
/Timestamp
в DateTime
в 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) {}
Ответ 3
Ответ @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;
}
}