Я хочу реализовать поиск диапазона дат с помощью 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()));
}
Или есть лучший способ выбрать этот диапазон?
Я имею в виду, что данные не возвращаются. Каким должен быть правильный способ выбора диапазона данных?
Я считаю, что вам нужно потратить некоторое время на переработку вашего поискового API. Как правило, когда кто-то хочет что-то найти по диапазону/интервалу, он предоставляет либо обе, либо только одну границу интервала, исходя из этого мы строим соответствующие ограничения, в вашем случае интервала нет вообще, значит, он нарушен по замыслу.
Хорошо, я буду использовать 2 переменные createdAtFrom
и createdAtTo
. Какой должен быть тип переменной и как реализовать интервальный поиск в методе getUsersBySpecification
?
Позвольте мне ответить шаг за шагом, основываясь на имеющемся у меня опыте (несколько различных корпоративных проектов). Дополнительное примечание: в основном я работал с 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
. Проще говоря, используйте современные версии.
Немного больше информации можно найти здесь.
LocalDateTime
и реализацию JPA 2.2 или выше, ваш код должен работать. Вы также можете проверить образцы кода здесь:
LocalDate
, но должно работать и с LocalDateTime
.Спасибо. Почему вы используете @DateTimeFormat(iso = DATE_TIME)
? Это обязательно?
Я использую @DateTimeFormat(iso = DATE_TIME)
, чтобы сообщить Spring, как анализировать параметры дохода LocalDateTime
. Без аннотации вы получите исключение. Немного больше информации вы можете найти здесь: https://www.baeldung.com/spring-date-parameters
. В статье перечислены некоторые другие способы парсинга. А также показана проблема парсинга параметров запроса в LocalDateTime
без аннотаций и дополнительных настроек
У меня есть один дополнительный вопрос: в моем случае, чтобы уменьшить код конечной точки, я отправляю объект Java UserSearchParams
с параметрами поиска (см. выше). Является ли код аннотации, который я добавил, действительным примером?
Да, должно работать нормально, я только что проверил, что в моем любимом проекте и параметры были успешно проанализированы
что вы имеете в виду под "не работает поиск"? ограничения
cb.lessThan
иcb.greaterThan
, созданные вашим кодом, являются взаимоисключающими.