2012-06-21 20 views
33

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

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

...more pairs possible 

Esto es lo que 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) 
} 

Me trataron de construir un gran Predicate, que contiene todos los demás predicados y añadir esto a la query.where(), pero de nuevo los predicados sobreescribe edad valores. Parece que no hay ninguna posibilidad de añadir una Predicate en lugar de cambio de un predicado :-(

El proyecto real es aún más complicado, debido a que algunos pares requiere una equal y algunos otros un like Y eso no es aún lo suficientemente:. Hay podría una declaración adicional con or incluido como type : 1;4;7 Aquí tiene el valor para dividir y crear una declaración como:.

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

UPDATE y SOLUCIÓN consiguió dos listas, la primera lista para Y funciona bien. Segunda lista contiene o frases 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(); 

Respuesta

43

Usted puede pasar una matriz de predicados a la CriteriaBuilder, decidir sobre equal o like a medida que avanza. Para esto, crea una lista y empaca el contenido de la lista en una matriz en una única declaración and. De esta manera:

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 distingiush entre and y or, utilizar dos listas.

+0

Bien, para mis declaraciones AND que funcionan. Creo una segunda lista y almacené mis declaraciones OR en esa lista. Al final, ¿qué hago? Copie ambos resultados de la lista en AND para ambos tipos de declaraciones. Mi lista 'orPredicates' contiene los elementos correctos. Incluso cuando solo agrego esta lista a la consulta, todas las declaraciones se juntan con AND. – Felix

+0

Puede crear una disyunción (o) de la misma manera que crea una conjunción (y) - llamando a los métodos 'o' y' y' en el CriteriaBuilder. El retorno es un solo 'Predicado' en ambos casos, así que si no tienes algún tipo de agrupación compleja, simplemente llama a' y' en los dos Predicados resultantes y usa el resultado en 'where' – kostja

+0

Tal vez lo obtuve ahora, actualicé la pregunta principal y publiqué mi solución. – Felix

16

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

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

Como alternativa, puede combinarlos en un solo predicado (tenga en cuenta que si no lo hace , que no es necesario para crear una conjunción como en el ejemplo) ;:

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

query.where(where); 
5

siempre he estado pensando que la creación de la solución de esa manera 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 corazón de la misma sólo tendrá la siguiente:

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.

0

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

Cuestiones relacionadas