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();
36
demandé sur Manuel Drieschmanns 2012-06-21 16:26:34

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.

45
répondu kostja 2012-06-21 12:32:08

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);
16
répondu axtavt 2012-06-21 12:34:46

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.

6
répondu Sasa7812 2014-11-24 03:53:37

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[])

0
répondu Rahul Jain 2016-11-07 04:17:23