← Назад к вопросам
Что можно использовать для запросов с несколькими параметрами
1.0 Junior🔥 221 комментариев
#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Запросы с несколькими параметрами в Java
Работу с множеством параметров можно организовать разными способами. Выбор зависит от контекста: HTTP запросы, поиск по фильтрам, конфигурация и т.д.
1. Query параметры в HTTP (GET запросы)
Spring MVC Controller
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
// Способ 1: Отдельные параметры
@GetMapping
public List<User> getUsersByParams(
@RequestParam(required = false) String name,
@RequestParam(required = false) String email,
@RequestParam(required = false) String status,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
// Логика фильтрации
return userService.searchUsers(name, email, status, page, size);
}
// Способ 2: Search DTO
@GetMapping("/search")
public List<User> search(UserSearchRequest request) {
return userService.search(request);
}
// Способ 3: Pageable и Specification
@GetMapping("/paginated")
public Page<User> getUsers(Pageable pageable) {
return userRepository.findAll(pageable);
}
}
// Search DTO
public class UserSearchRequest {
private String name;
private String email;
private String status;
private int page = 0;
private int size = 10;
// Getters, setters
}
Клиентская сторона (RestTemplate, WebClient)
@Service
public class UserClient {
@Autowired
private RestTemplate restTemplate;
// Способ 1: Конструирование URL
public List<User> searchUsers(String name, String email, String status) {
String url = String.format(
"/api/v1/users?name=%s&email=%s&status=%s",
name, email, status
);
ResponseEntity<List<User>> response = restTemplate.getForEntity(
url,
new ParameterizedTypeReference<List<User>>() {}
);
return response.getBody();
}
// Способ 2: UriComponentsBuilder
public List<User> searchUsersBuilder(UserSearchRequest request) {
String url = UriComponentsBuilder
.fromPath("/api/v1/users")
.queryParam("name", request.getName())
.queryParam("email", request.getEmail())
.queryParam("status", request.getStatus())
.queryParam("page", request.getPage())
.queryParam("size", request.getSize())
.toUriString();
ResponseEntity<List<User>> response = restTemplate.getForEntity(
url,
new ParameterizedTypeReference<List<User>>() {}
);
return response.getBody();
}
// Способ 3: WebClient (современный подход)
public Mono<List<User>> searchUsersWebClient(UserSearchRequest request) {
return webClient
.get()
.uri(uriBuilder -> uriBuilder
.path("/api/v1/users")
.queryParam("name", request.getName())
.queryParam("email", request.getEmail())
.queryParam("status", request.getStatus())
.queryParam("page", request.getPage())
.queryParam("size", request.getSize())
.build())
.retrieve()
.bodyToFlux(User.class)
.collectList();
}
}
2. POST запросы с телом (Request Body)
Spring MVC Controller
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
// Способ 1: DTO с аннотациями
@PostMapping("/search")
public List<User> searchByBody(@RequestBody UserSearchRequest request) {
return userService.search(request);
}
// Способ 2: Множество объектов
@PostMapping("/bulk-create")
public List<User> createMultiple(@RequestBody List<UserCreateRequest> requests) {
return userService.createBatch(requests);
}
}
// DTO для поиска
public class UserSearchRequest {
@NotBlank
private String name;
@Email
private String email;
@Pattern(regexp = "ACTIVE|INACTIVE|DELETED")
private String status;
@Min(0)
private int page = 0;
@Min(1)
@Max(100)
private int size = 10;
private LocalDate createdAfter;
private LocalDate createdBefore;
// Getters, setters
}
3. Спецификации для фильтрации (Specification Pattern)
// Entity
@Entity
public class User {
@Id
private Long id;
private String name;
private String email;
private String status;
private LocalDate createdAt;
}
// Specification
public class UserSpecifications {
public static Specification<User> hasName(String name) {
return (root, query, cb) -> {
if (name == null) return null;
return cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%");
};
}
public static Specification<User> hasEmail(String email) {
return (root, query, cb) -> {
if (email == null) return null;
return cb.equal(root.get("email"), email);
};
}
public static Specification<User> hasStatus(String status) {
return (root, query, cb) -> {
if (status == null) return null;
return cb.equal(root.get("status"), status);
};
}
public static Specification<User> createdAfter(LocalDate date) {
return (root, query, cb) -> {
if (date == null) return null;
return cb.greaterThanOrEqualTo(root.get("createdAt"), date);
};
}
}
// Repository
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
// Controller
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/search")
public Page<User> search(
@RequestParam(required = false) String name,
@RequestParam(required = false) String email,
@RequestParam(required = false) String status,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate createdAfter,
Pageable pageable) {
Specification<User> spec = Specification
.where(UserSpecifications.hasName(name))
.and(UserSpecifications.hasEmail(email))
.and(UserSpecifications.hasStatus(status))
.and(UserSpecifications.createdAfter(createdAfter));
return userRepository.findAll(spec, pageable);
}
}
4. QueryDSL для сложных запросов
// Зависимость в pom.xml
// <dependency>
// <groupId>com.querydsl</groupId>
// <artifactId>querydsl-jpa-apt</artifactId>
// </dependency>
@Configuration
public class QuerydslConfig {
@Bean
public JPAQueryFactory jpaQueryFactory(EntityManager em) {
return new JPAQueryFactory(em);
}
}
// Repository
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
@Autowired
private JPAQueryFactory queryFactory;
@Override
public Page<User> searchAdvanced(UserSearchRequest request, Pageable pageable) {
List<User> results = queryFactory
.selectFrom(QUser.user)
.where(
QUser.user.name.containsIgnoreCase(request.getName()),
QUser.user.email.eq(request.getEmail()),
QUser.user.status.eq(request.getStatus()),
QUser.user.createdAt.goe(request.getCreatedAfter())
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(QUser.user.createdAt.desc())
.fetch();
long total = queryFactory
.select(QUser.user.count())
.from(QUser.user)
.where(
QUser.user.name.containsIgnoreCase(request.getName()),
QUser.user.email.eq(request.getEmail())
)
.fetchOne();
return new PageImpl<>(results, pageable, total);
}
}
5. Search Request с фильтрами
public class AdvancedSearchRequest {
private String query; // Полнотекстовый поиск
private List<String> statuses; // Множественный выбор
private PriceRange priceRange; // Диапазон
private DateRange dateRange; // Диапазон дат
private List<String> tags; // Массив
private Map<String, String> customFilters; // Динамические фильтры
// Getters, setters
}
public class PriceRange {
private BigDecimal min;
private BigDecimal max;
// Getters, setters
}
public class DateRange {
private LocalDate from;
private LocalDate to;
// Getters, setters
}
// Использование
@Service
public class SearchService {
public List<Product> advancedSearch(AdvancedSearchRequest request) {
Specification<Product> spec = Specification.where(null);
// Полнотекстовый поиск
if (request.getQuery() != null) {
spec = spec.and((root, query, cb) ->
cb.or(
cb.like(cb.lower(root.get("name")), "%" + request.getQuery().toLowerCase() + "%"),
cb.like(cb.lower(root.get("description")), "%" + request.getQuery().toLowerCase() + "%")
)
);
}
// Множественные статусы
if (request.getStatuses() != null && !request.getStatuses().isEmpty()) {
spec = spec.and((root, query, cb) -> root.get("status").in(request.getStatuses()));
}
// Диапазон цены
if (request.getPriceRange() != null) {
PriceRange range = request.getPriceRange();
spec = spec.and((root, query, cb) ->
cb.between(root.get("price"), range.getMin(), range.getMax())
);
}
// Диапазон дат
if (request.getDateRange() != null) {
DateRange range = request.getDateRange();
spec = spec.and((root, query, cb) ->
cb.between(root.get("createdAt"), range.getFrom().atStartOfDay(), range.getTo().atTime(23, 59, 59))
);
}
return productRepository.findAll(spec);
}
}
6. Map для произвольных параметров
@PostMapping("/search")
public List<User> dynamicSearch(@RequestBody Map<String, Object> filters) {
// Использование Map когда набор параметров не фиксирован
String name = (String) filters.get("name");
String email = (String) filters.get("email");
Integer page = (Integer) filters.get("page");
return userService.search(name, email, page);
}
// Или через jackson для более типизированного подхода
@PostMapping("/search")
public List<User> search(@RequestBody JsonNode filters) {
String name = filters.get("name").asText();
String email = filters.get("email").asText();
return userService.search(name, email);
}
7. Лучшие практики
// ХОРОШО: DTO для HTTP параметров
public class SearchRequest {
@NotNull
private String query;
@Min(0)
private int page = 0;
@Min(1)
@Max(100)
private int size = 10;
@ValidSortOrder
private String sortOrder;
}
// ПЛОХО: множество параметров в контроллере
@GetMapping
public List<User> search(
String param1, String param2, String param3,
String param4, String param5, int page, int size) {
// Слишком много параметров
}
// ХОРОШО: использование Specification для фильтрации
public Page<User> search(UserSearchRequest request, Pageable pageable) {
Specification<User> spec = UserSpecifications.from(request);
return repository.findAll(spec, pageable);
}
// ХОРОШО: валидация входных данных
public void search(@Valid UserSearchRequest request) {
// Request уже валидирован
}
Сравнение подходов
| Подход | Простота | Тип | Случаи использования |
|---|---|---|---|
| @RequestParam | Очень высокая | Query | Простые параметры (1-3) |
| DTO + @RequestBody | Высокая | POST | Сложные фильтры, 3+ параметров |
| Specification | Средняя | Query/POST | Динамическая фильтрация |
| QueryDSL | Средняя | Query/POST | Очень сложные запросы |
| Map/JsonNode | Низкая | POST | Динамические параметры |
Лучшие практики
- Используй DTO для 3+ параметров — более читаемо
- Валидируй входные данные — используй @Valid
- Используй Specification для фильтрации — гибко и переиспользуемо
- Избегай Map если возможно — теряется типизация
- Используй Pageable для пагинации
- Документируй параметры — через @ApiParam или OpenAPI
- Кешируй запросы если результаты не меняются часто