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

标签 java spring postgresql java-8 spring-jdbc

我正在使用 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);
}

当然我得到了异常(exception):

...
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进行操作,因为这样更方便,并且能够持久化到数据库。我希望它应该默认启用。

最佳答案

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 接受),不仅仅是像 本地日期。一些行为将依赖于实现,因此只有 java.sql.* 几乎可以保证(粗略地说)。


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

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

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

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

关于java - Spring JDBC + Postgres SQL + Java 8 - 从/到 LocalDate 的转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25536969/

相关文章:

java - 如何在屏幕右侧设置 jFrame 位置?

java - 通过 https 的 REST API

java - 在 JPA/Hibernate 中执行只读操作的最佳实践是什么?

postgresql - 错误 : missing FROM-clause entry for table "new"

php - 如何同时更新和选择

postgresql - 从 postgresql 中的 jsonb 嵌套数组中删除键值对

java - 如何让报表页面方向变为 "rtl"?

java - 似乎无法将 Tomcat 日志重定向到 log4j

java - ZonedDateTime 的 Jackson 反序列化问题

java - 如何使用 Hibernate .xml 配置注入(inject) Spring Data JPA 但不使用 Spring Boot?