Реализовать поиск с диапазоном дат

Я хочу реализовать поиск диапазона дат с помощью Spring Boot. Я пробовал это:

адрес: localhost:8080/users/users?page=0&size=3&createdAt=2023-05-02T04:57:19.83795Z

Конечная точка:

    @GetMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
    public Page<UsersResource> getUsersBySearchSpecification(@Valid UserSearchParams params, Pageable pageable)
    {
        Page<UsersResource> list = userService.getUsersBySpecification(params, pageable);
        return list;
    }

Параметры поиска пользователя:

@Getter
@Setter
public class UserSearchParams {

    private String param1;

    private Integer param2;

    private List<String> param3;

    @DateTimeFormat(iso = DATE_TIME) 
    LocalDateTime startDate;

    private OffsetDateTime createdAt;
}

Функционал поиска:

    public Page<Users> getUsersBySpecification(UserSearchParams params, Pageable pageable)
    {
        Specification<Users> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (params.getCreatedAt() != null) {
                predicates.add(cb.greaterThan(root.get("createdAt"), params.getCreatedAt()));
            }
            if (params.getCreatedAt() != null) {
                predicates.add(cb.lessThan(root.get("createdAt"), params.getCreatedAt()));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };

    return userRepository.findAll(spec, pageable);
}

Функция поиска не работает. Какой тип данных я должен использовать для createdAt? OffsetDateTime или LocalDateTime лучшая альтернатива?

Должен ли я вручную анализировать значение 2023-05-02T04:57:19.83795Z или я могу полагаться на Spring Boot для анализа даты?

Правильно ли реализован этот код:

        if (params.getCreatedAt() != null) {
            predicates.add(cb.greaterThan(root.get("createdAt"), params.getCreatedAt()));
        }
        if (params.getCreatedAt() != null) {
            predicates.add(cb.lessThan(root.get("createdAt"), params.getCreatedAt()));
        }

Или есть лучший способ выбрать этот диапазон?

что вы имеете в виду под "не работает поиск"? ограничения cb.lessThan и cb.greaterThan, созданные вашим кодом, являются взаимоисключающими.

Andrey B. Panfilov 07.05.2023 14:41

Я имею в виду, что данные не возвращаются. Каким должен быть правильный способ выбора диапазона данных?

Peter Penzov 07.05.2023 14:44

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

Andrey B. Panfilov 07.05.2023 15:04

Хорошо, я буду использовать 2 переменные createdAtFrom и createdAtTo. Какой должен быть тип переменной и как реализовать интервальный поиск в методе getUsersBySpecification?

Peter Penzov 07.05.2023 15:07
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
4
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Позвольте мне ответить шаг за шагом, основываясь на имеющемся у меня опыте (несколько различных корпоративных проектов). Дополнительное примечание: в основном я работал с PostgreSQL в качестве базы данных.

  • Обычно используется LocalDateTime. Потому что с ним немного удобнее работать, у него нет информации о часовом поясе. Для баз данных обычно timestamp является подходящим типом столбца.

  • Spring Boot может анализировать параметры запроса до типа LocalDateTime без явных пользовательских реализаций конфигурации. Вот пример запроса типа LocalDateTime:

    • https://localhost:8080/my/endpoint?startDate=2023-01-01T00:30:00.000&endDate=2023-12-31T23:30:00.000
    • https://localhost:8080/my/endpoint?startDate=2023-01-01T00%3A30%3A00.000&endDate=2023-12-31T23%3A30%3A00.000 (html декодирован)

    Вот соответствующий код контроллера:

    @GetMapping(value = "/my/endpoint")
    public void myMethod(
            @RequestParam(name = "startDate") @DateTimeFormat(iso = DATE_TIME) LocalDateTime startDate,
            @RequestParam(name = "endDate") @DateTimeFormat(iso = DATE_TIME) LocalDateTime endDate) {
        return doSearch(startDate, endDate);
    }

Но будьте осторожны со старыми версиями Spring. По моему опыту, это должно работать, начиная с Spring Boot 2.2.X. Идея состоит в том, чтобы реализовать JPA выше версии 2.1, потому что в версии 2.1 нет удобного использования LocalDateTime. Проще говоря, используйте современные версии.

Немного больше информации можно найти здесь.

Спасибо. Почему вы используете @DateTimeFormat(iso = DATE_TIME)? Это обязательно?

Peter Penzov 07.05.2023 22:37

Я использую @DateTimeFormat(iso = DATE_TIME), чтобы сообщить Spring, как анализировать параметры дохода LocalDateTime. Без аннотации вы получите исключение. Немного больше информации вы можете найти здесь: https://www.baeldung.com/spring-date-parameters. В статье перечислены некоторые другие способы парсинга. А также показана проблема парсинга параметров запроса в LocalDateTime без аннотаций и дополнительных настроек

HereAndBeyond 08.05.2023 08:27

У меня есть один дополнительный вопрос: в моем случае, чтобы уменьшить код конечной точки, я отправляю объект Java UserSearchParams с параметрами поиска (см. выше). Является ли код аннотации, который я добавил, действительным примером?

Peter Penzov 08.05.2023 15:54

Да, должно работать нормально, я только что проверил, что в моем любимом проекте и параметры были успешно проанализированы

HereAndBeyond 08.05.2023 16:03

Другие вопросы по теме