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

我正在使用Postgres SQL 9.2,Spring JDBC 4.0.5版和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); } 

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

 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 getAll() { final String sql = "select * from public.TABLE_A"; final BeanPropertyRowMapper rowMapper = new BeanPropertyRowMapper(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 getAll() { final String sql = "select * from public.TABLE_A"; final BeanPropertyRowMapper rowMapper = new BeanPropertyRowMapper(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 convert(Object o, Class 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 下载页面与JDBC的兼容性4.2新function仅从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得到支持,您可以增强未来的重构function,并减少开发期间所需的代码量。

您还可以查看一些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.