query criteriabuilder java jpa jpa-2.0 criteria-api

java - criteriabuilder - jpa query



Realmente dinĂ¡mico JPA CriteriaBuilder (4)

Para Predicado puede crear una nueva Lista de Arrays, agregar todo su predicado a la lista y luego, al final, puede crear una matriz de predicados y convertir la lista a una matriz de predicados y asignarla directamente a criteriabuilder.where (new Pridicate [])

Necesito crear un JPA CriteriaBuilder dinámico "real". Obtengo un Map<String, String> con las declaraciones. Parece que:

name : John surname : Smith email : [email protected] ...more pairs possible

Esto es lo que implemento:

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 un Predicate grande, que contiene todos los demás predicados y lo query.where() a la query.where() , pero de nuevo los predicados sobrescriben los valores anteriores. Parece que no hay posibilidad de agregar un Predicate lugar de cambiar un predicado :-(

El proyecto real es aún más complicado, porque algunos pares requieren un equal y otros like . Y eso ni siquiera es suficiente: podría haber una declaración extra con or incluido como type : 1;4;7 . Aquí el valor debe dividirse y crear una declaración como:

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

ACTUALIZACIÓN y SOLUCIÓN Tiene dos listas, la primera Lista para AND funciona bien. La segunda lista contiene sentencias OR como se esperaba:

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


Puede pasar una matriz de predicados a CriteriaBuilder , decidiendo equal o like sobre la marcha. Para esto, construya una lista y empaquete los contenidos de la lista en una matriz en una sola instrucción. Me gusta esto:

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 necesite distinguir entre and y or , use dos listas.


Siempre he pensado que la creación de una solución como esa es como reinventar la bicicleta. Aquí https://github.com/sasa7812/jfbdemo es mi solución. Fue probado en EclipseLink e Hibernate (EclipseLink en producción, lo usamos en varios proyectos para casos simples). A veces solo necesitas una solución rápida para hacer una tabla de datos con clasificación y filtrado, nada sofisticado. Puede filtrar y ordenar en campos unidos, e incluso en colecciones. El proyecto contiene una demostración en Primefaces que muestra las capacidades de FilterCriteriaBuilder. En el fondo, solo necesitas 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 usa en algún lugar para continuar mi trabajo en esta biblioteca.


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

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

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

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