Vraiment dynamique JPA CriteriaBuilder
je dois créer un" vrai "JPA dynamique CriteriaBuilder
. Je reçois un Map<String, String>
avec les déclarations. On dirait:
name : John
surname : Smith
email : email@email.de
...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 ajouter cela à la query.where()
, mais encore une fois les prédicats overwrites anciennes valeurs. On dirait qu'il n'y a aucune possibilité d'ajouter un Predicate
au lieu de changer 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 déclaration supplémentaire avec or
inclus comme type : 1;4;7
. Ici, la valeur doit se diviser et créer une déclaration comme:
<rest of statement> AND (type = 1 OR type = 4 OR type = 7)
UPDATE and SOLUTION A deux listes, la première Liste ET fonctionne bien. Deuxième liste contient ou des déclarations 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();
4 réponses
vous pouvez passer un tableau de prédicats au CriteriaBuilder
, en décidant sur equal
ou like
que vous allez. Pour cela, construisez une liste et empaquetez le contenu de la liste dans un tableau en une seule déclaration and
. Comme ceci:
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 une distinction entre and
et or
, utilisez deux listes.
une option est d'utiliser le fait que la méthode avec le 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 hibernation (EclipseLink en production, nous l'avons utilisé dans plusieurs projets pour des cas simples). Parfois, vous avez juste besoin d'une solution rapide pour faire un datable avec le tri et le filtrage, rien de fantaisiste. Il est capable de filtrer et de trier les champs joints, et même sur collection. Le projet contient une démo sur Primefaces montrant les capacités de FilterCriteriaBuilder. Au fond de lui 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 de quelqu'un pour me dire que c'est utile et est utilisé quelque part pour continuer mon travail sur cette bibliothèque.
Pour Prédicat vous pouvez créer une nouvelle liste de tableaux ajouter tous vos prédicat de la liste et puis à la fin, vous pouvez créer un prédicat de tableau et de liste conversion de prédicat tableau et affecter directement à criteriabuilder.où (nouveau Prix[])