Spring JDBC+Postgres SQL+Java 8-从LocalDate到LocalDate的转换



我使用的是Postgres SQL 9.2、版本为4.0.5的Spring JDBC和Java 8
Java8引入了新的日期/时间API,我想使用它,但遇到了一些困难。我已经创建了表格table_A:

CREATE TABLE "TABLE_A"
(
  new_date date,
  old_date date
)

我正在使用Spring JDBC与数据库进行通信。我已经创建了Java类,它对应于这个表:

public class TableA
{
    private LocalDate newDate;
    private Date oldDate;
    //getters and setters
}

这是我的代码,负责插入新行:

public void create(TableA tableA)
{
    BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(tableA);
    final String sql = "INSERT INTO public.TABLE_A (new_date,old_date) values(:newDate,:oldDate)";
    namedJdbcTemplate.update(sql,parameterSource);
}

当我执行这个方法时,我得到了异常:

org.postgresql.util.PSQLException: Can't infer the SQL type to use for an instance of java.time.LocalDate. Use setObject() with an explicit Types value to specify the type to use.

所以我更新了BeanPropertySqlParameterSource:的版本

BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(tableA);
parameterSource.registerSqlType("newDate", Types.DATE); 

在那个次更改之后,我可以插入行。但接下来,我想从数据库中提取行。这是我的方法:

public List<TableA> getAll()
{
    final String sql = "select * from public.TABLE_A";
    final BeanPropertyRowMapper<TableA> rowMapper = new BeanPropertyRowMapper<TableA>(TableA.class);
    return namedJdbcTemplate.query(sql,rowMapper);
}

当然我也有例外:

...
at org.springframework.beans.BeanWrapperImpl.convertIfNecessary(BeanWrapperImpl.java:474)
at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:511)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1119)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:902)
at org.springframework.jdbc.core.BeanPropertyRowMapper.mapRow(BeanPropertyRowMapper.java:255)
...
Caused by: java.lang.IllegalStateException: Cannot convert value of type [java.sql.Date] to required type [java.time.LocalDate] for property 'newDate': no matching editors or conversion strategy found.

所以我更新了我的代码,这次是BeanPropertyRowMapper,我向bean包装器添加了转换服务,它能够执行从java.sql.Date到java.time.LocalDate 的转换

public List<TableA> getAll()
{
    final String sql = "select * from public.TABLE_A";
    final BeanPropertyRowMapper<TableA> rowMapper = new BeanPropertyRowMapper<TableA>(TableA.class)
    {
        @Override
        protected void initBeanWrapper(BeanWrapper bw) {
            super.initBeanWrapper(bw);
           bw.setConversionService(new ConversionService() {
               @Override
               public boolean canConvert(Class<?> aClass, Class<?> aClass2) {
                   return aClass == java.sql.Date.class && aClass2 == LocalDate.class;
               }
               @Override
               public boolean canConvert(TypeDescriptor typeDescriptor, TypeDescriptor typeDescriptor2) {
                   return canConvert(typeDescriptor.getType(), typeDescriptor2.getType());
               }
               @Override
               public <T> T convert(Object o, Class<T> tClass) {
                   if(o instanceof Date && tClass == LocalDate.class)
                   {
                       return (T)((Date)o).toLocalDate();
                   }
                   return null;

       }
           @Override
           public Object convert(Object o, TypeDescriptor typeDescriptor, TypeDescriptor typeDescriptor2) {
               return convert(o,typeDescriptor2.getType());
           }
       });
    }
}   ;
return namedJdbcTemplate.query(sql,rowMapper);

现在一切正常,但相当复杂
实现这一点更容易吗?一般来说,我希望在Java代码中操作LocalDate,因为它更方便,并且能够将其持久化到数据库。我希望它应该在默认情况下启用。

新日期&JEP170:JDBC4.2定义了API对JDBC的支持。Postgres下载页面与JDBC4.2的兼容性新功能仅从Postgres 9.4版本开始,因此使用新的API和旧的驱动程序会出现一些兼容性挑战。

即使是setObject(1, new java.util.Date());也被Postgres中的相同约束所拒绝(MySQL很乐意接受),而不仅仅是像LocalDate这样的新API。有些行为将依赖于实现,所以(粗略地说)只有java.sql.*是可以保证的。


至于Spring JDBC框架,我认为重写它的行为可以绕过它,以后不会后悔。对于你已经做的事情,我建议一个稍微不同的方法:

  1. 扩展CCD_ 4行为以使用新日期&时间API以及与参数输入相关联的其他类(如果需要的话)(我不熟悉那个Spring API)
  2. BeanPropertyRowMapper已经被覆盖的行为提取到另一个类中进行获取操作
  3. 用工厂模式或实用程序类将其全部封装起来,这样您就不必再看它了

这样,如果API得到支持,就可以增强未来的重构功能,并减少开发过程中所需的代码量。

您还可以查看一些DAO方法。

请注意,支持Java 8日期和时间API(JSR-310),但实现尚未完成:https://jdbc.postgresql.org/documentation/head/8-date-time.html报价:

Note that ZonedDateTime, Instant and OffsetTime / TIME [ WITHOUT TIMEZONE ] are not supported.

最新更新