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, я думаю, что переопределение его поведения работает, чтобы обойти его, не сожалея об этом позже. Я предлагаю несколько иной подход к тому, что вы уже сделали:
- расширения
BeanPropertySqlParameterSource
поведение для работы с новым API даты и времени и другими классами, связанными с вводом параметров, если это необходимо (я не знаком с этой весной ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС.) - извлеките уже переопределенное поведение
BeanPropertyRowMapper
в другой класс для извлечения операций. - оберните все это заводским шаблоном или служебным классом, чтобы вам не пришлось смотреть на него снова.
таким образом, вы улучшаете будущие возможности рефакторинга, если 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.