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