48

「実際の」動的JPAを作成する必要がありますCriteriaBuilder。私Map<String, String>はステートメントで取得します。次のようになります。

name : John
surname : Smith
email : email@email.de

...more pairs possible

これが私が実装するものです:

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

Predicate他のすべての述語を含むbigを作成し、これをに追加しようとしましたquery.where()が、述語は古い値を上書きします。述語を変更する代わりに追加する可能性はないようですPredicate:-(

equal一部のペアにはとが必要なため、実際のプロジェクトはさらに複雑likeです。そして、それだけでは十分ではありません。orのようなインクルードを含む追加のステートメントが存在する可能性がありtype : 1;4;7ます。ここで、値を分割して、次のようなステートメントを作成する必要があります。

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

更新と解決策 2つのリストがあります。最初のリストはANDでうまく機能します。2番目のリストには、exspectedのようなORステートメントが含まれています。

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

5 に答える 5

62

またはをCriteriaBuilder決定しながら、述語の配列を に渡すことができます。このために、リストを作成し、リストの内容を 1 つのステートメントで配列にパックします。このような:equallikeand

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

andとを区別する必要がある場合はor、2 つのリストを使用します。

于 2012-06-21T12:32:08.493 に答える
22

1 つのオプションは、可変数の引数を持つメソッドが配列を取ることができるという事実を使用することです。

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

または、それらを単一の述語に結合することもできます (そうしない場合は、例のように接続詞を作成する必要がないことに注意してください);:

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

query.where(where);
于 2012-06-21T12:34:46.110 に答える
6

そのようなソリューションを生み出すことは、自転車を再発明するようなものだと私は常々考えてきました。ここでhttps://github.com/sasa7812/jfbdemoが私のソリューションです。EclipseLink と Hibernate でテストされました (EclipseLink は本番環境で、いくつかのプロジェクトで単純なケースで使用されました)。場合によっては、並べ替えとフィルタリングを使用して dataTable を作成するための簡単なソリューションが必要な場合があります。結合されたフィールドだけでなく、コレクションに対してもフィルター処理と並べ替えを行うことができます。プロジェクトには、FilterCriteriaBuilder の機能を示す Primefaces のデモが含まれています。その中心には、これが必要です:

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

データベースから結果を取得します。

このライブラリでの作業を継続するために、それが便利でどこかで使用されていることを教えてくれる人が本当に必要です。

于 2014-11-24T03:53:37.870 に答える
1

次のように Predicate ... に変換できます

query.where( predicates.stream().toArray( Predicate[]::new ) );
于 2019-10-04T21:35:47.097 に答える