Realmente dinámico JPA CriteriaBuilder

Necesito crear un «real» dinámica JPA CriteriaBuilder. Puedo obtener una Map<String, String> con las declaraciones. Se parece a:

name : John
surname : Smith
email : [email protected].de

...more pairs possible

Aquí es lo que puedo implementar:

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)
}

Traté de construir una gran Predicate, que contiene todos los otros predicados y agregar esto a la query.where(), pero de nuevo los predicados sobrescribe los valores antiguos. Parece que no hay posibilidad de añadir un Predicate en lugar de cambiar de un Predicado 🙁

El proyecto de real es aún más complicado, ya que algunos de los pares requiere una equal y algunos otros un like. Y que no es suficiente ni siquiera: es posible que una instrucción adicional con or incluido como type : 1;4;7. Aquí el valor que tienen para dividir y crear una instrucción como:

<rest of statement> AND (type = 1 OR type = 4 OR type = 7)

ACTUALIZACIÓN y SOLUCIÓN de
Tengo dos listas, la primera de la Lista Y funciona bien. La segunda lista contiene O declaraciones como 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();
InformationsquelleAutor Felix | 2012-06-21

4 Kommentare

  1. 55

    Se puede pasar un array de predicados a la CriteriaBuilder, decidir sobre equal o like como usted va. Para ello, construir una lista y el paquete de los contenidos de la lista en una matriz en una sola and declaración. Como este:

    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);

    En caso de que usted necesita para distingiush entre and y or, el uso de dos listas.

    • Bien, para mi Y declaraciones que trabajar. Puedo crear una segunda Lista y almacenados mi O declaraciones en esa lista. Al final, tengo que hacer ¿qué? Copia tanto de la lista de togehter resultados en Y para ambos tipos de declaraciones. Mi orPredicates lista contiene los elementos correctos. Incluso, cuando yo sólo añadir a esta lista a la consulta, todas las declaraciones de que junto con el Y el.
    • Usted puede crear una disyunción (o) de la misma manera se crea una conjunción (y) – llamando or y and métodos en el CriteriaBuilder. El retorno es una sola Predicate en ambos casos – así que si usted no tiene algún tipo de complejo de agrupación, simplemente llame a and en los dos resultante de los Predicados y utilizar el resultado en la where
    • Tal vez tengo ahora, he actualizado a la pregunta principal y post mi solución.
    • thanku.. a mí me funciona
  2. 18

    Una opción es utilizar el hecho de que el método con número variable de argumentos puede tomar una matriz:

    query.where(predicates.toArray(new Predicate[predicates.size()])); 

    Alternativamente, usted puede combinarlos en un único predicado (tenga en cuenta que si usted no lo hace, usted no necesita crear una conjunción como en tu ejemplo);:

    Predicate where = cb.conjunction();
    while (column.hasNext()) {
        ...
        where = cb.and(where, cb.equal(userRoot.get(colIndex), colValue));
    }
    
    query.where(where);
  3. 7

    Siempre he sido de pensar que la creación de la solución, como que es como reinventar la bicicleta.
    Aquí https://github.com/sasa7812/jfbdemo es mi solución. Fue probado en EclipseLink y de Hibernación
    (EclipseLink en la producción, que se utiliza en varios proyectos para casos sencillos).
    A veces sólo necesitas una solución rápida para hacer una tabla de datos con la ordenación y filtrado, nada de lujo.
    Es capaz de filtrar y ordenar los campos combinados, e incluso en las colecciones.
    Contiene el proyecto de demostración en Primefaces mostrando las habilidades de FilterCriteriaBuilder.
    En el corazón de la misma, usted sólo necesita esto:

     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();
    }

    para obtener los resultados de la base de datos.

    Realmente necesito que alguien me diga que es útil y se utiliza en algún lugar para continuar mi trabajo en esta biblioteca.

  4. 0

    Para Predicado puede crear nuevos ArrayList agregar todas tus predicado a la lista y, a continuación, al final se puede crear un predicado matriz y convertir lista para predicado matriz y asignar directamente a criteriabuilder.donde(nueva Pridicate[])

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea