当前位置: 首页 > 面试题库 >

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

谭桐
2023-03-14
问题内容

我正在使用Postgres SQL 9.2,带有4.0.5版本的Spring JDBC和Java8。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进行操作,因为它更加方便,并且能够将其持久化到database
。我希望它应该默认启用。


问题答案:

JEP 170:JDBC 4.2定义了对JDBC的新的Date&Date
API支持。Postgres 下载页面与JDBC
4.2新功能的兼容性仅从Postgres 9.4版本开始,因此某些兼容性挑战将通过使用具有较旧驱动程序的新API弹出。

setObject(1, new java.util.Date());在Postgres中,Even
被相同的约束所拒绝(MySQL很高兴地接受了它),不仅是新的API,如LocalDate。有些行为将取决于实现,因此只能java.sql.*保证(大致而言)。

至于Spring JDBC框架,我认为重写其行为可以绕过它,而不必以后再后悔。对于您已经做过的事情,我建议采取一种稍微不同的方法

  1. 扩展BeanPropertySqlParameterSource行为以使用新的日期和时间API,以及在需要时与其他与参数输入相关联的类一起使用(我不熟悉该Spring API)。
  2. 将已经被覆盖的行为提取BeanPropertyRowMapper到另一个类中以进行提取操作。
  3. 用工厂模式或实用程序类将它们包装起来,因此您无需再次查看它。

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

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



 类似资料:
  • 我是java.time包的新手。我有一个本地日期是2015-12-10。我需要将此转换为ZonedDateTime。时间应该是00:00:00,区域是zoneoffset.utc。 dateTimeException:无法从java.time.localDate类型的TemporalAccessor:2015-12-10获取Instant 我也试过: 这会产生意想不到的结果。 我查看了API并尝试

  • 我在将时间戳对象转换为joda的LocalTime时遇到问题。 参见以下示例: 我无法确定为什么“new LocalDate(t)”会导致“1111-11-17”。有人能帮我吗? 在使用joda time hibernate填充bean的LocalDate类型属性时,我注意到了这个“问题”。

  • 在Java8中,如何将(在中)转换为(在中)?

  • 我有一个来自源代码的,根据规范,它应该是,但在类中没有看到任何方法将转换为。 最好的方法是什么?

  • 在尝试将转换为时,我遇到以下异常。

  • 问题内容: 有没有简单的方法可以将(Java 8中引入的)转换为对象? “简单”是指比这更简单: 对我来说似乎有点尴尬。 由于我们仅对日期部分感兴趣,并且两个对象都没有时区信息,因此为什么要明确引入时区?转换应隐式采用午夜时间和系统默认时区。 问题答案: 其实有。有一个静态方法的valueOf在这不正是那个对象。所以我们有 就是这样。没有明确设置时区,因为本地时区是隐式获取的。 从文档: 提供的L