2012-06-21 36 views
33

Tôi cần tạo một JPA động "thực" động CriteriaBuilder. Tôi nhận được một số Map<String, String> với các câu lệnh. Dường như:Tiêu chí JPA thực sự năng độngBuilder

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

...more pairs possible 

Dưới đây là những gì tôi thực hiện:

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

Tôi cố gắng để xây dựng một lớn Predicate, trong đó có tất cả các vị từ khác và thêm video này vào các query.where(), nhưng một lần nữa các vị từ ghi đè cũ giá trị. Hình như không có khả năng để thêm một Predicate thay vì thay đổi một Predicate :-(

Dự án thực thậm chí còn phức tạp hơn, bởi vì một số cặp yêu cầu một equal và một số khác một like Và đó là thậm chí không đủ. Có một tuyên bố thêm với or có thể bao gồm như type : 1;4;7 Ở đây, giá trị phải chia tay và tạo ra một tuyên bố như sau:.

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

UPDATE và SOLUTION Got hai danh sách, Danh sách đầu tiên cho AND hoạt động tốt. danh sách thứ hai chứa OR như 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(); 

Trả lời

43

Bạn có thể vượt qua một loạt các vị đến CriteriaBuilder, quyết định equal hoặc like như bạn đi. Đối với điều này, xây dựng một danh sách và đóng gói các nội dung của danh sách vào một mảng trong một tuyên bố and duy nhất. Như thế này:

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

Trong trường hợp bạn cần phải distingiush giữa andor, sử dụng hai danh sách.

+0

Được rồi, đối với các câu lệnh AND của tôi hoạt động. Tôi tạo một Danh sách thứ hai và lưu trữ các câu lệnh OR của tôi trong danh sách đó. Cuối cùng, tôi làm gì? Sao chép cả hai kết quả togehter danh sách trong AND cho cả hai loại câu lệnh. Danh sách 'orPredicates' của tôi chứa các phần tử chính xác. Ngay cả khi tôi chỉ thêm danh sách này vào truy vấn, tất cả các câu lệnh được đặt cùng với AND. – Felix

+0

Bạn có thể tạo ra một disjunction (hoặc) giống như cách bạn tạo một kết hợp (và) - bằng cách gọi 'hoặc' và' và 'phương pháp trên CriteriaBuilder. Sự trở lại là một 'Predicate' duy nhất trong cả hai trường hợp - vì vậy nếu bạn không có một số nhóm phức tạp, chỉ cần gọi' và' trên hai kết quả Predicates và sử dụng kết quả trong 'where' – kostja

+0

Có lẽ tôi đã nhận nó ngay bây giờ, tôi đã cập nhật câu hỏi chính và đăng giải pháp của tôi. – Felix

16

Một lựa chọn là sử dụng thực tế là phương pháp với số biến của tham số có thể mất một mảng:

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

Ngoài ra, bạn có thể kết hợp chúng thành một vị duy nhất (lưu ý rằng nếu bạn không làm điều đó , bạn không cần phải tạo một kết hợp như trong ví dụ của bạn) ;:

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

query.where(where); 
5

Tôi luôn nghĩ rằng việc tạo ra giải pháp như vậy giống như phát minh lại chiếc xe đạp. Ở đây https://github.com/sasa7812/jfbdemo là giải pháp của tôi. Nó đã được thử nghiệm trên EclipseLink và Hibernate (EclipseLink trong sản xuất, chúng tôi sử dụng nó trong một số dự án cho các trường hợp đơn giản). Đôi khi bạn chỉ cần một giải pháp nhanh chóng để tạo một DataTable với phân loại và lọc, không có gì lạ mắt. Có thể lọc và sắp xếp trên các trường đã tham gia và thậm chí trên các bộ sưu tập. Project chứa demo trên Primefaces thể hiện khả năng của FilterCriteriaBuilder. Ở trung tâm của nó, bạn chỉ cần điều này:

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

để nhận kết quả từ cơ sở dữ liệu.

Tôi thực sự cần ai đó nói với tôi rằng nó hữu dụng và được sử dụng ở đâu đó để tiếp tục công việc của tôi trên thư viện này.

0

Đối với Predicate bạn có thể tạo ArrayList mới thêm tất cả vị từ của bạn vào danh sách và sau đó bạn có thể tạo mảng vị từ và chuyển danh sách thành mảng vị ngữ và gán trực tiếp nó vào criteriabuilder.where (new Pridicate [])

Các vấn đề liên quan