Passer au contenu principal

Interrogation des données 25.02

Ouvrir dans ChatGPT

L'interface QueryableRepository étend Repository avec des requêtes avancées à travers RepositoryCriteria. Contrairement aux dépôts de base qui ne prennent en charge que le filtrage simple, les dépôts interrogeables fournissent des requêtes structurées avec des types de filtres personnalisés, du tri et de la pagination.

Comprendre les types de filtres

QueryableRepository introduit un deuxième paramètre générique pour le type de filtre : QueryableRepository<T, F>T est votre type d'entité et F est votre type de filtre personnalisé.

Cette séparation existe parce que différentes sources de données utilisent des langages de requête différents :

// Filtres prédicats pour des collections en mémoire
QueryableRepository<Product, Predicate<Product>> inMemoryRepo =
new CollectionRepository<>(products);

// Objets de filtre personnalisés pour des API REST ou bases de données
QueryableRepository<User, UserFilter> apiRepo =
new DelegatingRepository<>(/* implémentation */);

// Requêtes de chaînes pour moteurs de recherche
QueryableRepository<Document, String> searchRepo =
new DelegatingRepository<>(/* implémentation */);

CollectionRepository utilise Predicate<Product> car il filtre des objets Java en mémoire. Le dépôt d'API REST utilise UserFilter - une classe personnalisée avec des champs comme department et status qui correspondent aux paramètres de requête. Le dépôt de recherche utilise des chaînes simples pour des requêtes en texte intégral.

Les composants de l'interface utilisateur ne se soucient pas de ces différences. Ils appellent setBaseFilter() avec le type de filtre que le dépôt attend, et le dépôt gère la traduction.

Construction de requêtes avec des critères de dépôt

RepositoryCriteria regroupe tous les paramètres de requête dans un seul objet immuable. Au lieu d'appeler des méthodes séparées pour le filtre, le tri et la pagination, vous passez tout en une seule fois :

// Requête complète avec tous les paramètres
RepositoryCriteria<Product, Predicate<Product>> criteria =
new RepositoryCriteria<>(
20, // décalage - ignorer les 20 premiers
10, // limite - prendre 10 éléments
orderCriteria, // règles de tri
product -> product.getPrice() < 100.0 // condition de filtre
);

// Exécuter la requête
Stream<Product> results = repository.findBy(criteria);
int totalMatching = repository.size(criteria);

La méthode findBy() exécute la requête complète - elle applique le filtre, trie les résultats, ignore le décalage et prend la limite. La méthode size() compte tous les éléments correspondant au filtre, en ignorant la pagination.

Vous pouvez également créer des critères avec seulement les parties dont vous avez besoin :

// Filtre uniquement
RepositoryCriteria<Product, Predicate<Product>> filterOnly =
new RepositoryCriteria<>(product -> product.isActive());

// Pagination uniquement
RepositoryCriteria<Product, Predicate<Product>> pageOnly =
new RepositoryCriteria<>(0, 25);

Travailler avec différents types de filtres

Filtres prédicats

Pour les collections en mémoire, utilisez Predicate<T> pour composer des filtres fonctionnels :

CollectionRepository<Product> repository = new CollectionRepository<>(products);

// Construire des prédicats complexes
Predicate<Product> activeProducts = product -> product.isActive();
Predicate<Product> inStock = product -> product.getStock() > 0;
Predicate<Product> affordable = product -> product.getPrice() < 50.0;

// Combiner des conditions
repository.setBaseFilter(activeProducts.and(inStock).and(affordable));

// Filtrage dynamique
Predicate<Product> filter = product -> true;
if (categoryFilter != null) {
filter = filter.and(p -> p.getCategory().equals(categoryFilter));
}
if (maxPrice != null) {
filter = filter.and(p -> p.getPrice() <= maxPrice);
}
repository.setBaseFilter(filter);

Objets de filtre personnalisés

Les sources de données externes ne peuvent pas exécuter les prédicats Java. Au lieu de cela, vous créez des classes de filtre qui représentent ce que votre backend peut rechercher :

public class ProductFilter {
private String category;
private BigDecimal maxPrice;
private Boolean inStock;

// accesseurs et mutateurs...
}

// Utiliser avec le dépôt personnalisé
ProductFilter filter = new ProductFilter();
filter.setCategory("Électronique");
filter.setMaxPrice(new BigDecimal("99.99"));
filter.setInStock(true);

RepositoryCriteria<Product, ProductFilter> criteria =
new RepositoryCriteria<>(filter);

Stream<Product> results = customRepository.findBy(criteria);

À l'intérieur de la méthode findBy() de votre dépôt personnalisé, vous traduirez cet objet de filtre :

  • Pour les API REST : Convertir en paramètres de requête comme ?category=Électronique&maxPrice=99.99&inStock=true
  • Pour SQL : Construire une clause where comme WHERE category = ? AND price <= ? AND stock > 0
  • Pour GraphQL : Construire une requête avec les sélections de champs appropriées

L'implémentation de Repository doit gérer cette traduction, gardant ainsi votre code UI propre.

Tri des données

OrderCriteria définit comment trier vos données. Chaque OrderCriteria a besoin d'un fournisseur de valeur (comment obtenir la valeur de votre entité) et d'une direction :

// Tri d'un seul champ
OrderCriteria<Employee, String> byName =
new OrderCriteria<>(Employee::getName, OrderCriteria.Direction.ASC);

// Tri multi-niveaux - département d'abord, puis salaire, puis nom
OrderCriteriaList<Employee> sorting = new OrderCriteriaList<>();
sorting.add(new OrderCriteria<>(Employee::getDepartment, OrderCriteria.Direction.ASC));
sorting.add(new OrderCriteria<>(Employee::getSalary, OrderCriteria.Direction.DESC));
sorting.add(new OrderCriteria<>(Employee::getName, OrderCriteria.Direction.ASC));

// Utiliser dans les critères
RepositoryCriteria<Employee, Predicate<Employee>> criteria =
new RepositoryCriteria<>(0, 50, sorting, employee -> employee.isActive());

Le fournisseur de valeur (Employee::getName) fonctionne pour le tri en mémoire. Mais les sources de données externes ne peuvent pas exécuter des fonctions Java. Dans ces cas, OrderCriteria accepte un nom de propriété :

// Pour les dépôts externes - fournir à la fois le getter de valeur et le nom de propriété
OrderCriteria<Employee, String> byName = new OrderCriteria<>(
Employee::getName, // Pour le tri en mémoire
Direction.ASC,
null, // Comparateur personnalisé (optionnel)
"name" // Nom de la propriété pour le tri backend
);

CollectionRepository utilise le fournisseur de valeur pour trier des objets Java. Les implémentations de DelegatingRepository peuvent utiliser le nom de la propriété pour construire des clauses de commande en SQL ou sort=name:asc dans les API REST.

Contrôler la pagination

Définissez le décalage et la limite pour contrôler quelle tranche de données charger :

// Pagination basée sur des pages
int page = 2; // numéro de page basé sur zéro
int pageSize = 20; // éléments par page
int offset = page * pageSize;

RepositoryCriteria<Product, Predicate<Product>> criteria =
new RepositoryCriteria<>(offset, pageSize, null, yourFilter);

// Chargement progressif - charger plus de données de manière incrémentielle
int currentlyLoaded = 50;
int loadMore = 25;

repository.setOffset(0);
repository.setLimit(currentlyLoaded + loadMore);