Переопределение BeanPropertyRowMapper для поддержки JodaTime DateTime - PullRequest
11 голосов
/ 11 октября 2011

Объект My Domain имеет несколько полей Joda-Time DateTime. Когда я читаю значения базы данных, используя SimpleJdbcTemplate:

Пациент пациент = jdbc.queryForObject (sql, новый BeanPropertyRowMapper (Patient.class), PatientId);

Это просто не удается, и на удивление, никаких ошибок не было зарегистрировано. Я думаю, это из-за синтаксического анализа timestamp на DateTime не работает с Jdbc.

Если возможно унаследовать и переопределить BeanPropertyRowMapper и дать указание преобразовать все java.sql.Timestamp и java.sql.Date в DateTime, это было бы здорово и могло бы сэкономить много дополнительного кода.

Любой совет?

Ответы [ 3 ]

22 голосов
/ 12 октября 2011

Правильнее всего сделать подкласс 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 голосов
/ 12 октября 2011

Глядя на реализацию 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 roperty d escriptor", которое используется (pd.getPropertyType()) в JdbcUtils как введите поля для сопоставления.

Если вы посмотрите на код JdbcUtils для метода getResultSetValue, вы увидите, что он просто переходит от одного оператора if к другому, чтобы сопоставить pd.getPropertyType() со всеми стандартными типами. Когда он не находит его, поскольку DateTime не является «стандартным» типом, он опирается на rs.getObject():

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

Затем, если этот объект является датой SQL, он преобразует его в 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) {}
1 голос
/ 23 июля 2014

Ответ @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;
  }
}
...