← Назад к вопросам

Что можно использовать для запросов с несколькими параметрами

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Динамические параметры

Лучшие практики

  1. Используй DTO для 3+ параметров — более читаемо
  2. Валидируй входные данные — используй @Valid
  3. Используй Specification для фильтрации — гибко и переиспользуемо
  4. Избегай Map если возможно — теряется типизация
  5. Используй Pageable для пагинации
  6. Документируй параметры — через @ApiParam или OpenAPI
  7. Кешируй запросы если результаты не меняются часто