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);

}

когда я выполнил этот метод я получил исключение:

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); 

после этого я смог вставить строку. Но затем я хотел бы получить строки из базы данных. Вот мой метод:--10-->

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, которая может выполнять преобразование из Ява.язык SQL.Дата на java.время.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);

и теперь все работает, но это довольно сложно.
Легче ли этого добиться? Вообще говоря, я хотел бы работать с LocalDate в моем Java-коде, потому что это намного удобнее, и иметь возможность сохранять его в базе данных. Я ожидал бы, что он должен быть включен по умолчанию.

2 ответов


новая дата и дата поддержка API с JDBC определяется JEP 170: JDBC 4.2. Postgres страница скачать совместимость с новыми функциями JDBC 4.2 начинается только с версии Postgres 9.4, поэтому некоторые проблемы совместимости будут появляться с использованием нового API со старыми драйверами.

даже setObject(1, new java.util.Date()); отклоняется тем же ограничением в Postgres (который с радостью принимается MySQL), а не только новый API, как LocalDate. Некоторые модели поведения будут реализованы зависимый, так только java.sql.* гарантируется в значительной степени (грубо говоря).


Что касается Spring JDBC framework, я думаю, что переопределение его поведения работает, чтобы обойти его, не сожалея об этом позже. Я предлагаю несколько иной подход к тому, что вы уже сделали:

  1. расширения BeanPropertySqlParameterSource поведение для работы с новым API даты и времени и другими классами, связанными с вводом параметров, если это необходимо (я не знаком с этой весной ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС.)
  2. извлеките уже переопределенное поведение BeanPropertyRowMapper в другой класс для извлечения операций.
  3. оберните все это заводским шаблоном или служебным классом, чтобы вам не пришлось смотреть на него снова.

таким образом, вы улучшаете будущие возможности рефакторинга, если API получает поддержку и уменьшает количество кода, необходимого во время разработки.

вы также можете посмотреть на некоторые Дао подходы.


обратите внимание, что Java 8 Date and Time 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.