简体   繁体   English

真正动态的 JPA CriteriaBuilder

[英]Really dynamic JPA CriteriaBuilder

I need to create a "real" dynamic JPA CriteriaBuilder .我需要创建一个“真正的”动态 JPA CriteriaBuilder I get an Map<String, String> with the statements.我得到一个带有语句的Map<String, String> It looks like:看起来像:

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

...more pairs possible

Here is what i implement:这是我实现的:

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

I tried to build a big Predicate , which contains all other predicates and add this to the query.where() , but again the predicates overwrites old values.我尝试构建一个大Predicate ,其中包含所有其他谓词并将其添加到query.where() ,但谓词再次覆盖旧值。 Looks like there is no possibility to add a Predicate instead of change a Predicate :-(看起来不可能添加Predicate而不是更改谓词:-(

The real project is even more complicated, because some pairs requires an equal and some other a like .真正的项目更复杂,因为有些对需要equal ,有些需要 a like And that is not even enough: There could a extra statement with or included like type : 1;4;7 .这还不够:可能有一个额外的语句,包含or包含type : 1;4;7 Here the value have to split up and create a statement like:这里的值必须拆分并创建如下语句:

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

UPDATE and SOLUTION Got two lists, first List for AND works well. UPDATE 和 SOLUTION有两个列表,第一个 List for AND 效果很好。 Second list contains OR statements like exspected:第二个列表包含 OR 语句,如expected:

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

You can pass an array of predicates to the CriteriaBuilder , deciding on equal or like as you go.您可以将一组谓词传递给CriteriaBuilder ,在您进行时决定equallike For this, build a list and pack the contents of the list into an array in a single and statement.为此,构建一个列表并将列表的内容打包到单个and语句中的数组中。 Like this:像这样:

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

In case you need to distingiush between and and or , use two lists.如果您需要区分andor ,请使用两个列表。

One option is to use the fact that method with variable number of arguments can take an array:一种选择是使用具有可变数量参数的方法可以采用数组的事实:

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

Alternatively, you can combine them into a single predicate (note that if you don't do it, you don't need to create a conjunction as in your example);:或者,您可以将它们组合成一个谓词(请注意,如果您不这样做,则无需像示例中那样创建连词);:

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

query.where(where);

I have always been thinking that creation of solution like that is like reinventing the bicycle.我一直认为创造这样的解决方案就像重新发明自行车。 Here https://github.com/sasa7812/jfbdemo is my solution.这里https://github.com/sasa7812/jfbdemo是我的解决方案。 It was tested on EclipseLink and Hibernate (EclipseLink in production, we used it in several projects for simple cases).它在 EclipseLink 和 Hibernate 上进行了测试(生产中的 EclipseLink,我们在几个项目中使用它用于简单的情况)。 Sometimes you just need a quick solution to make a dataTable with sorting and filtering, nothing fancy.有时你只需要一个快速的解决方案来制作一个带有排序和过滤的数据表,没什么特别的。 It is able to filter and sort on joined fields, and even on collections.它能够对连接的字段甚至集合进行过滤和排序。 Project contains demo on Primefaces showing the abilities of FilterCriteriaBuilder.项目包含关于 Primefaces 的演示,展示了 FilterCriteriaBuilder 的能力。 In the heart of it you just need this:在它的核心,你只需要这个:

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

to get the results from database.从数据库中获取结果。

I really need someone to tell me that it is usefull and is used somewhere to continue my work on this library.我真的需要有人告诉我它很有用并且在某个地方用于继续我在这个库上的工作。

您可以转换为 Predicate ... 如下

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

对于 Predicate,您可以创建新的 ArrayList 将所有谓词添加到列表中,然后最后您可以创建一个谓词数组并将列表转换为谓词数组并直接将其分配给 criteriabuilder.where(new Pridicate[])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM