【问题标题】:Spring JDBC + Postgres SQL + Java 8 - conversion from/to LocalDateSpring JDBC + Postgres SQL + Java 8 - 从/到 LocalDate 的转换
【发布时间】:2014-10-21 14:49:10
【问题描述】:

我正在使用 Postgres SQL 9.2、4.0.5 版的 Spring JDBC 和 Java 8。
Java 8 引入了新的日期/时间 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 wrapper 添加了转换服务,它能够执行从 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进行操作,因为它更方便,并且可以持久化到数据库中。我希望它应该默认启用。

【问题讨论】:

    标签: java spring postgresql java-8 spring-jdbc


    【解决方案1】:

    JDBC 的新日期和日期 API 支持由 JEP 170: JDBC 4.2 定义。 Postgres download page 与 JDBC 4.2 新功能的兼容性仅从 Postgres 版本 9.4 开始,因此使用旧驱动程序的新 API 会出现一些兼容性挑战。

    即使setObject(1, new java.util.Date()); 也被 Postgres 中的相同约束拒绝(MySQL 很高兴地接受了),不仅仅是像 LocalDate 这样的新 API。某些行为将取决于实现,因此几乎可以保证java.sql.*(粗略地说)。


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

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

    通过这种方式,如果 API 得到支持,您可以增强未来的重构能力,并减少开发期间所需的代码量。

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

    【讨论】:

    • 我修复了我的 org.postgresql.util.PSQLException:无法推断用于 java.time.LocalDate 实例的 SQL 类型。使用带有显式 Types 值的 setObject() 来指定要使用的类型 异常将我的 postgres JDBC 从 9.4 Build 1206 更新到 9.4.1212 版本
    【解决方案2】:

    请注意支持 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.
    

    【讨论】:

    • 链接描述没有改变。你的意思是说它不再是真的了?
    猜你喜欢
    • 1970-01-01
    • 2017-11-30
    • 2015-11-07
    • 1970-01-01
    • 2015-05-23
    • 1970-01-01
    • 2014-05-23
    • 2017-03-29
    • 1970-01-01
    相关资源
    最近更新 更多