2012-06-21 12 views
33

Potrzebuję stworzyć "prawdziwy" dynamiczny JPA CriteriaBuilder. Otrzymuję Map<String, String> z instrukcjami. Wygląda to tak:Naprawdę dynamiczne JPA CriteriaBuilder

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

...more pairs possible 

Oto co wdrażać:

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

Starałem się zbudować duży Predicate, który zawiera wszystkie inne predykaty i dodać to do query.where(), ale znowu predykaty nadpisuje stary wartości. Wygląda na to, nie ma możliwości, aby dodać Predicate zamiast zmiany orzecznika :-(

Prawdziwy projekt jest jeszcze bardziej skomplikowana, ponieważ niektóre pary wymaga equal a niektóre drugi like I to nie wystarczy nawet. Nie może extra sprawozdanie z or zawarta podobnie type : 1;4;7 Tutaj wartość mają się rozdzielić i stworzyć podobnego oświadczenia.

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

UPDATE i rozwiązanie Got dwie listy, pierwsza lista dla AND działa dobrze. Druga lista zawiera lub stwierdzenia jak 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(); 

Odpowiedz

43

Można przekazać tablicę orzeczników do CriteriaBuilder, decydując się na equal lub like jak przejść. W tym celu utwórz listę i spakuj zawartość listy do tablicy w jednym oświadczeniu and. Tak:

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

W przypadku trzeba distingiush między and i or użyć dwóch list.

+0

OK, dla moich instrukcji ORAZ, które działają. Tworzę drugą listę i zapisuję moje instrukcje OR na tej liście. Na koniec robię co? Skopiuj oba wyniki z listy do AND dla obu typów instrukcji. Moja lista 'orPredicates' zawiera poprawne elementy. Nawet jeśli dodaję tę listę tylko do zapytania, wszystkie instrukcje zostaną połączone z AND. – Felix

+0

Możesz utworzyć rozdzielenie (lub) w ten sam sposób, w jaki tworzysz spójnik (i) - przez wywołanie metod 'lub' i' and' w CriteriaBuilder. Zwrot jest pojedynczym "Predykatem" w obu przypadkach - więc jeśli nie masz jakiegoś skomplikowanego grupowania, po prostu wywołaj 'i' na dwóch wynikowych predykatach i użyj wyniku w 'where' – kostja

+0

Może mam teraz to, ja zaktualizowałem główne pytanie i opublikowałem moje rozwiązanie. – Felix

16

Jedną z opcji jest wykorzystanie faktu, że metoda ze zmienną liczbą argumentów można wziąć tablicę:

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

Alternatywnie, można połączyć je w jeden predykat (Zauważ, że jeśli nie rób tego , nie trzeba tworzyć koniunkcja jak w przykładzie) ;:

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

query.where(where); 
5

ja zawsze myślałem, że stworzenie takiego rozwiązania jest jak odkrywanie roweru. Tutaj https://github.com/sasa7812/jfbdemo jest moim rozwiązaniem. Został przetestowany na EclipseLink i Hibernate (EclipseLink w produkcji, użyliśmy go w kilku projektach dla prostych przypadków). Czasami po prostu potrzebujesz szybkiego rozwiązania, aby utworzyć tabelę danych z sortowaniem i filtrowaniem, nic nadzwyczajnego. Umożliwia filtrowanie i sortowanie na połączonych polach, a nawet na kolekcjach. Projekt zawiera wersję demonstracyjną na Primefaces pokazujący możliwości FilterCriteriaBuilder. W sercu to wystarczy to:

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

aby uzyskać wyniki z bazy danych.

Naprawdę potrzebuję kogoś, kto powie mi, że jest użyteczny i jest gdzieś wykorzystywany do kontynuowania mojej pracy nad tą biblioteką.

0

Dla Predicate można utworzyć nową ArrayList dodać wszystkie swoje orzeczenie na liście, a następnie na koniec można utworzyć tablicę źródłowe i listy konwertować do tablicy i orzekać bezpośrednio przypisać go do criteriabuilder.where (nowa Pridicate [])

Powiązane problemy