Filtros e Paginação

Filtros e Paginação

Como implementar filtros e paginação usando @Filterable, GenericFilterDTO e GenericSpecificationsBuilder.

Estrutura básica
  1. Defina um DTO de filtro que implemente GenericFilterDTO
public class FuncionarioFilterDTO implements GenericFilterDTO {
  @Filterable(operation = Filterable.FilterOperation.LIKE)
  private String nome;

  @Filterable(operation = Filterable.FilterOperation.EQUAL)
  private String departamento;

  // getters/setters
}
  1. O service base resolve as Specifications automaticamente
public class FuncionarioService extends AbstractBaseCrudService<Funcionario, FuncionarioDTO, Long, FuncionarioFilterDTO> {
  public FuncionarioService(BaseCrudRepository<Funcionario, Long> repo, Class<Funcionario> entityClass) {
    super(repo, entityClass);
  }
}
  1. Utilize os endpoints de filtragem do controller base
POST /api/human-resources/funcionarios/filter?page=0&size=20
Content-Type: application/json

{ "nome": "ana", "departamento": "RH" }
Ordenação com filtros
Boas práticas
Modo estrito para payload de range (recomendado enterprise)

Por que este filtro é diferente (zero boilerplate, pronto pra produção)

Dicas de UI para filtros com enums grandes

Enums em SELECTs (sem endpoint)

Operações de Filtro Suportadas
Operação Descrição Exemplo DTO
EQUAL Igualdade @Filterable(EQUAL)
NOT_EQUAL Diferente @Filterable(NOT_EQUAL)
LIKE Contém (ci) @Filterable(LIKE)
NOT_LIKE Não contém (ci) @Filterable(NOT_LIKE)
STARTS_WITH Começa com (ci) @Filterable(STARTS_WITH)
ENDS_WITH Termina com (ci) @Filterable(ENDS_WITH)
GREATER_THAN Maior que @Filterable(GREATER_THAN)
GREATER_OR_EQUAL Maior ou igual @Filterable(GREATER_OR_EQUAL)
LESS_THAN Menor que @Filterable(LESS_THAN)
LESS_OR_EQUAL Menor ou igual @Filterable(LESS_OR_EQUAL)
IN Pertence a uma lista @Filterable(IN)
NOT_IN Não pertence a uma lista @Filterable(NOT_IN)
BETWEEN Entre (parcial ou completo) @Filterable(BETWEEN)
IS_NULL É nulo (usar Boolean TRUE no DTO) @Filterable(IS_NULL) + Boolean campo
IS_NOT_NULL Não é nulo (usar Boolean TRUE no DTO) @Filterable(IS_NOT_NULL) + Boolean

Lote 2 — Intervalos/Data/Lista/Coleção/Booleanos

Lote 1 (Core) — Operações Adicionadas

Operação Descrição Exemplo DTO
BETWEEN_EXCLUSIVE Entre exclusivo: > a AND < b @Filterable(BETWEEN_EXCLUSIVE)
NOT_BETWEEN Negação do between (inclusive) @Filterable(NOT_BETWEEN)
OUTSIDE_RANGE Fora do intervalo: < min OR > max @Filterable(OUTSIDE_RANGE)
ON_DATE Igual à data (parte de data) @Filterable(ON_DATE) + LocalDate
IN_LAST_DAYS Nos últimos N dias @Filterable(IN_LAST_DAYS) + Integer dias
IN_NEXT_DAYS Nos próximos N dias @Filterable(IN_NEXT_DAYS) + Integer dias
SIZE_EQ Tamanho de coleção igual a N @Filterable(SIZE_EQ) + Integer
SIZE_GT Tamanho de coleção maior que N @Filterable(SIZE_GT) + Integer
SIZE_LT Tamanho de coleção menor que N @Filterable(SIZE_LT) + Integer
IS_TRUE Campo booleano verdadeiro @Filterable(IS_TRUE)
IS_FALSE Campo booleano falso @Filterable(IS_FALSE)

Notas: - Para ON_DATE, use LocalDate no DTO. A comparação considera o intervalo [início do dia, início do dia seguinte). - Para IN_LAST_DAYS/IN_NEXT_DAYS, o valor é relativo ao horário atual (UTC) e converte para Instant. - Para SIZE_*, aplique apenas em atributos de coleção (OneToMany/ManyToMany); o builder usa CriteriaBuilder.size. - Requisito de coleção em SIZE_*: defina relation para apontar explicitamente para o atributo de coleção na entidade. Se o caminho não for coleção, o builder lança erro informativo. - Para novos componentes compactos de superfície, prefira controlType canônico com a família INLINE_*.

Notas de Timezone

Notas: - Operações com ci (case-insensitive) normalizam usando lower(). - Para IS_NULL/IS_NOT_NULL, sugere-se modelar no DTO como Boolean campoIsNull; quando true, o predicado é aplicado. - Para IS_TRUE/IS_FALSE, o predicado é aplicado quando o campo do DTO está presente (não nulo). Recomenda‑se enviar true para indicar que o predicado deve ser considerado.

Exemplos práticos (DTO + chamadas)

DTO de filtro (vendas)

import org.praxisplatform.uischema.filter.annotation.Filterable;
import java.time.LocalDate;
import java.util.List;

public class VendaFilterDTO implements org.praxisplatform.uischema.filter.dto.GenericFilterDTO {
  @Filterable(operation = Filterable.FilterOperation.LIKE)
  private String cliente;

  @Filterable(operation = Filterable.FilterOperation.GREATER_OR_EQUAL)
  private java.math.BigDecimal valorMin;

  @Filterable(operation = Filterable.FilterOperation.LESS_OR_EQUAL)
  private java.math.BigDecimal valorMax;

  @Filterable(operation = Filterable.FilterOperation.BETWEEN)
  private List<LocalDate> emissaoEntre; // [de, ate]

  @Filterable(operation = Filterable.FilterOperation.IN)
  private List<String> canais; // ["ONLINE","LOJA"]

  @Filterable(operation = Filterable.FilterOperation.LIKE, relation = "vendedor.nome")
  private String vendedorNome;

  @Filterable(operation = Filterable.FilterOperation.IS_TRUE)
  private Boolean pago;
}
POST /api/vendas/filter?page=0&size=20
Content-Type: application/json

{
  "cliente": "maria",
  "valorMin": 100.00,
  "valorMax": 1000.00,
  "emissaoEntre": ["2024-01-01", "2024-12-31"],
  "canais": ["ONLINE", "LOJA"],
  "vendedorNome": "silva",
  "pago": true
}

Vantagem competitiva (por números)

Resultado: menos boilerplate, tempo de entrega menor e APIs/UX mais consistentes — alinhadas com as melhores práticas do ecossistema Spring + OpenAPI.

Exemplos autocontidos de modelagem

Sugestoes de combinacao para FilterDTO sem depender de app externo:

Exemplos tipicos:

Exemplos rápidos (payloads reais)
POST /api/human-resources/missoes/filter
Content-Type: application/json

{ "statusIn": ["ABERTA", "EM_ANDAMENTO"] }
POST /api/human-resources/veiculos/filter
Content-Type: application/json

{ "statusNotIn": ["INATIVO"] }
POST /api/human-resources/incidentes/filter
Content-Type: application/json

{ "ocorridoEmOn": "2025-03-01" }
POST /api/human-resources/sinais-socorro/filter
Content-Type: application/json

{ "abertoEmLastDays": 7 }
Referências