J'ai besoin de créer un "vrai" JPA dynamique CriteriaBuilder
. J'obtiens un Map<String, String>
Avec les instructions. Ça ressemble à:
name : John
surname : Smith
email : [email protected]
...more pairs possible
Voici ce que j'implémente:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> userRoot = query.from(User.class);
query.select(userRoot);
List<Predicate> predicates = new ArrayList<Predicate>();
Iterator<String> column = statements.keySet().iterator();
while (column.hasNext()) {
// get the pairs
String colIndex = column.next();
String colValue = statements.get(colIndex);
// create the statement
Predicate pAnd = cb.conjunction();
pAnd = cb.and(pAnd, cb.equal(userRoot.get(colIndex), colValue));
predicates.add(pAnd);
}
// doesn't work, i don't know how many predicates i have -> can not address them
query.where(predicates.get(0), predicates.get(1), ...);
// doesn't work, because it is a list of predicates
query.where(predicates);
// doesn't work, because the actual predicate overwrites the old predicate
for (Predicate pre : predicates) {
query.where(pre)
}
J'ai essayé de construire un grand Predicate
, qui contient tous les autres prédicats et l'ajouter à la query.where()
, mais encore une fois, les prédicats écrasent les anciennes valeurs. Il semble qu'il n'y ait aucune possibilité d'ajouter un Predicate
au lieu de modifier un prédicat :
Le vrai projet est encore plus compliqué, car certaines paires nécessitent un equal
et d'autres un like
. Et ce n'est même pas suffisant: il pourrait y avoir une instruction supplémentaire avec or
incluse comme type : 1;4;7
. Ici, la valeur doit être divisée et créer une déclaration comme:
<rest of statement> AND (type = 1 OR type = 4 OR type = 7)
--- (MISE À JOUR et SOLUTION J'ai deux listes, la première liste pour AND fonctionne bien. La deuxième liste contient OR instructions comme exspected:
final List<Predicate> andPredicates = new ArrayList<Predicate>();
final List<Predicate> orPredicates = new ArrayList<Predicate>();
for (final Entry<String, String> entry : statements.entrySet()) {
final String colIndex = entry.getKey();
final String colValue = entry.getValue();
if (colIndex != null && colValue != null) {
if (!colValue.contains(";")) {
if (equals) {
andPredicates.add(cb.equal(userRoot.get(colIndex), colValue));
} else {
andPredicates.add(cb.like(userRoot.<String> get(colIndex), "%" + colValue + "%"));
}
} else {
String[] values = colValue.split(";");
for (String value : values) {
orPredicates.add(cb.or(cb.equal(userRoot.get(colIndex), value)));
}
}
}
}
// Here goes the magic to combine both lists
if (andPredicates.size() > 0 && orPredicates.size() == 0) {
// no need to make new predicate, it is already a conjunction
query.where(andPredicates.toArray(new Predicate[andPredicates.size()]));
} else if (andPredicates.size() == 0 && orPredicates.size() > 0) {
// make a disjunction, this part is missing above
Predicate p = cb.disjunction();
p = cb.or(orPredicates.toArray(new Predicate[orPredicates.size()]));
query.where(p);
} else {
// both types of statements combined
Predicate o = cb.and(andPredicates.toArray(new Predicate[andPredicates.size()]));
Predicate p = cb.or(orPredicates.toArray(new Predicate[orPredicates.size()]));
query.where(o, p);
}
query.where(predicates.toArray(new Predicate[predicates.size()]));
users = em.createQuery(query).getResultList();
Vous pouvez transmettre un tableau de prédicats au CriteriaBuilder
, en décidant de equal
ou like
au fur et à mesure. Pour cela, créez une liste et regroupez le contenu de la liste dans un tableau dans une seule instruction and
. Comme ça:
final List<Predicate> predicates = new ArrayList<Predicate>();
for (final Entry<String, String> e : myPredicateMap.entrySet()) {
final String key = e.getKey();
final String value = e.getValue();
if ((key != null) && (value != null)) {
if (value.contains("%")) {
predicates.add(criteriaBuilder.like(root.<String> get(key), value));
} else {
predicates.add(criteriaBuilder.equal(root.get(key), value));
}
}
}
query.where(criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()])));
query.select(count);
Si vous devez faire la distinction entre and
et or
, utilisez deux listes.
Une option consiste à utiliser le fait qu'une méthode avec un nombre variable d'arguments peut prendre un tableau:
query.where(predicates.toArray(new Predicate[predicates.size()]));
Alternativement, vous pouvez les combiner en un seul prédicat (notez que si vous ne le faites pas, vous n'avez pas besoin de créer une conjonction comme dans votre exemple) ;:
Predicate where = cb.conjunction();
while (column.hasNext()) {
...
where = cb.and(where, cb.equal(userRoot.get(colIndex), colValue));
}
query.where(where);
J'ai toujours pensé que créer une telle solution, c'est comme réinventer le vélo. Ici https://github.com/sasa7812/jfbdemo est ma solution. Il a été testé sur EclipseLink et Hibernate (EclipseLink en production, nous l'avons utilisé dans plusieurs projets pour des cas simples). Parfois, vous avez juste besoin d'une solution rapide pour créer un tableau de données avec tri et filtrage, rien de compliqué. Il est capable de filtrer et de trier sur les champs joints, et même sur les collections. Le projet contient une démo sur Primefaces montrant les capacités de FilterCriteriaBuilder. Au cœur de celui-ci, vous avez juste besoin de ceci:
public List<T> loadFilterBuilder(int first, int pageSize, Map<String, Boolean> sorts, List<FieldFilter> argumentFilters, Class<? extends AbstractEntity> entityClass) {
FilterCriteriaBuilder<T> fcb = new FilterCriteriaBuilder<>(getEntityManager(), (Class<T>) entityClass);
fcb.addFilters(argumentFilters).addOrders(sorts);
Query q = getEntityManager().createQuery(fcb.getQuery());
q.setFirstResult(first);
q.setMaxResults(pageSize);
return q.getResultList();
}
pour obtenir les résultats de la base de données.
J'ai vraiment besoin que quelqu'un me dise que c'est utile et qu'il est utilisé quelque part pour continuer mon travail sur cette bibliothèque.
Pour Predicate, vous pouvez créer un nouveau ArrayList, ajoutez tous vos prédicats à la liste, puis à la fin, vous pouvez créer un tableau de prédicats et convertir la liste en tableau de prédicats et l'affecter directement à criteriabuilder.where (nouveau Pridicate [])