java.time.LocalDate не поддерживается в собственных запросах последними Spring Data/Hibernate?

Проблема: Собственные запросы с датами возврата Spring Data возвращают java.sql.Date, а не java.time.LocalDate, несмотря на настройку.

Контекст: новый проект с Spring Boot 2.0.0.M5 (последний), Hibernate 5.2.11, Hibernate-Java8 5.2.12 (который обеспечивает поддержку классов JSR310, пока он находится в пути к классам).

Анонимизированный пример ниже (приложение не совсем о днях рождения):

public interface BirthdayRepository<T, ID extends Serializable> extends Repository<T, ID> {
    @Query(value = "select day from birthdays", nativeQuery = true)
    Iterable<java.sql.Date> getBirthdays(); //the return type should ideally be java.time.LocalDate
}

В базе данных (SQL Server) поле дня — это DATE, а значения — 2017-10-24.

Проблема в том, что во время выполнения репозиторий данных Spring (чью реализацию я не могу контролировать или есть способ?) возвращает java.sql.Date, а не java.time.LocalDate (Пояснение: тип возвращаемого значения выглядит будет решаться Spring Data и останется java.sql.Date, даже если я изменю тип возвращаемого значения на java.time.LocalDate, как я и начал).

Есть ли способ получить LocalDate напрямую? Я могу преобразовать его позже, но (1) это неэффективно и (2) методы репозитория должны возвращать старые классы даты/времени, а это кое-что Я хотел бы избежать. Я прочитал документацию Spring Data, но ничего об этом.

РЕДАКТИРОВАТЬ: для тех, у кого есть тот же вопрос, ниже приведено решение, конвертер, предложенный Йенсом.

public class LocalDateTypeConverter {

    @Converter(autoApply = true)
    public static class LocalDateConverter implements AttributeConverter<LocalDate, Date> {

        @Nullable
        @Override
        public Date convertToDatabaseColumn(LocalDate date) {
            return date == null ? null : new Date(LocalDateToDateConverter.INSTANCE.convert(date).getTime());
        }

        @Nullable
        @Override
        public LocalDate convertToEntityAttribute(Date date) {
            return date == null ? null : DateToLocalDateConverter.INSTANCE.convert(date);
        }
    }

person wishihadabettername    schedule 24.10.2017    source источник
comment
Я в замешательстве. Вы указали в своем интерфейсе, что хотите Iterable<java.sql.Date> почему вы ожидаете что-то еще? Почему методы репозитория должны возвращать старые классы даты/времени?   -  person Jens Schauder    schedule 25.10.2017
comment
Это то, что возвращает Spring Data. Если я поставлю LocalDate (что я и сделал изначально), во время выполнения произойдет сбой с ClassCastException, когда я извлеку элемент из итератора. Поэтому я и поставил тот комментарий, что тип должен быть. Добавлю уточнение.   -  person wishihadabettername    schedule 25.10.2017


Ответы (2)


Похоже, вы нашли брешь в преобразователях. Spring Data конвертирует по умолчанию между java.util.Date и java.time.LocalDate, но не между java.time.LocalDate и java.sql.Date и другими типами, связанными с датой и временем, в пакете java.sql.

Вы можете создать свой собственный конвертер, чтобы сделать это. Вы можете использовать Jsr310JpaConverters в качестве шаблона.

Кроме того, вы можете создать запрос функции, и если вы создадите конвертер для своего использования, вы можете даже отправить запрос на включение.

person Jens Schauder    schedule 25.10.2017
comment
Спасибо, Джен. Не могли бы вы уточнить, если известно, почему Spring Data (или это драйвер) выбирает java.sql.Date, а не, скажем, java.util.Date или java.time.Date? Тип поля в базе данных — дата (что для SQL Server означает просто ГГГГ-ММ-ДД). Как из трех возможностей выбрать именно эту? Кроме того, учитывая, что запрос является родным, как указать Spring Data использовать его для возвращаемого типа? Аннотировать нечего, как в случае с сущностями и столбцами JPA. Спасибо. - person wishihadabettername; 25.10.2017
comment
Не заглядывая в код ни на какие детали: Spring Data, вероятно, просто берет то, что драйвер предоставляет по умолчанию, а затем пытается преобразовать это в требуемый тип с помощью своих преобразователей. Но в этом случае подходящего преобразователя нет. - person Jens Schauder; 25.10.2017
comment
Я подозреваю, что логика сопоставления преобразователя смотрит на один и тот же класс, а не на подклассы. java.sql.Date является подклассом java.util.Date (полезное обсуждение здесь так что конвертер для последнего должен/может активироваться, но он не активируется, тогда я займусь конвертером. - person wishihadabettername; 25.10.2017
comment
И конвертер заработал, так что приму ответ полностью. - person wishihadabettername; 25.10.2017

Я знаю, что это старый вопрос, но для моего решения этой проблемы не требуется специальный конвертер.

    public interface BirthdayRepository<T, ID extends Serializable> extends Repository<T, ID> {
      @Query(value = "select cast(day as date) from birthdays", nativeQuery = true)
      Iterable<java.time.LocalDate> getBirthdays();
    }

CAST указывает JPQL использовать доступные типы даты\времени Java, а не java.sql.Date

person habelson    schedule 19.09.2019
comment
Прохладно! Работает очень хорошо! - person Thomas Lang; 18.12.2019