通过用户定义的动态where子句条件在JPA中创建高级搜索

问题描述 投票:0回答:2

我想创建一个搜索,用户在其中定义 where 子句条件。如果用户在搜索表单中输入参数,则该参数将包含在 WHERE 子句中。如果该参数留空,则 where 子句中将省略该参数。如果整个表单为空,则查询应返回 SELECT ALL 和整个表。这样,用户输入的参数越多,查询逐渐变得更加精确。

我正在尝试下面的代码,但这似乎需要输入参数,并且仅当实际数据与指定的所有谓词匹配时才返回结果。看起来谓词被包含在数组中,即使它们是 NULL 和空?要么就是我在写这篇文章时遗漏了一些非常明显的东西。

我的问题是:

1) 如果搜索表单留空,谓词是否会被排除在数组之外?

2)如果是,查询不是默认为 SELECT ALL 吗? [query.from(Contacts.class)]?

我看到了一个类似的问题,但并没有真正告诉我如何实现上述目标,因为看起来我的代码逻辑是正确的。

JPA where 子句任意

预先感谢您的指导!

public ListDataModel<Contacts> qSearchContacts(String firstName, String surName, Integer countryid, Integer companyId) {

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Contacts> query = cb.createQuery(Contacts.class);
    Root<Contacts> cont = query.from(Contacts.class);
    query.select(cont);

    List<Predicate> predicateList = new ArrayList<Predicate>();
    Predicate firstnamePredicate, surnamePredicate, countryPredicate, companyPredicate;

    if ((firstName != null) && (!(firstName.isEmpty()))) {
        firstnamePredicate = cb.like(cb.upper(cont.<String>get("firstName")), "%" + firstName.toUpperCase() + "%");
        predicateList.add(firstnamePredicate);
    }


    if ((surName != null) && (!(surName.isEmpty()))) {
        surnamePredicate = cb.like(cb.upper(cont.<String>get("surName")), "%" + surName.toUpperCase() + "%");
        predicateList.add(surnamePredicate);

     }

    if (countryid != null) {
        Join<Contacts, Country> country = cont.join("country");
        countryPredicate = cb.equal(country.<Integer>get("countryid"), countryid);
        predicateList.add(countryPredicate);
    } 
    }

    if (companyId != null) {
        Join<Contacts, Company> company = cont.join("company");
        companyPredicate = cb.equal(company.<Integer>get("companyId"), companyId);
        predicateList.add(companyPredicate);
    }

    Predicate[] predicateArray = new Predicate[predicateList.size()];
    predicateList.toArray(predicateArray);
    query.where(predicateArray);
    ListDataModel<Contacts> contactResultList = new ListDataModel<Contacts>(em.createQuery(query).getResultList());

    return contactResultList;
}
java mysql jpa orm eclipselink
2个回答
1
投票

我解决了这个问题。上面的代码似乎在所有情况下都创建了谓词,并且没有明确的条件从数组列表中排除谓词或在不存在谓词的情况下执行 SELECT ALL。我使用 TypedQuery 对象明确地重写了代码中的每个参数。现在这导致了所需的行为。没有谓词会导致 SELECT ALL。每个谓词仅在不为 null 且不为空时才包含为 WHERE 子句条件。希望这有帮助。

public ListDataModel<Contacts> qSearchContacts(String firstName, String surName, Integer countryid, Integer companyId) {

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Contacts> query = cb.createQuery(Contacts.class);
    Root<Contacts> cont = query.from(Contacts.class);
    query.select(cont);

    // Join<Contacts, Country> country = cont.join("country", JoinType.LEFT);
    List<Predicate> predicateList = new ArrayList<Predicate>();

    if ((firstName != null) && (!(firstName.isEmpty()))) {
        ParameterExpression<String> p
                = cb.parameter(String.class, "firstName");
        predicateList.add(cb.like(cb.upper(cont.<String>get("firstName")), "%" + firstName.toUpperCase() + "%"));

    }

    if ((surName != null) && (!(surName.isEmpty()))) {
        ParameterExpression<String> p
                = cb.parameter(String.class, "surName");
        predicateList.add(cb.like(cb.upper(cont.<String>get("surName")), "%" + surName.toUpperCase() + "%"));

    }

    if ((countryid != null) && (countryid != 0)) {
        Join<Contacts, Country> country = cont.join("country");
        ParameterExpression<Integer> ci
                = cb.parameter(Integer.class, "countryid");
        predicateList.add(cb.equal(country.get("countryid"), ci));
    }

    if ((companyId != null) && (companyId != 0)) {
        Join<Contacts, Company> company = cont.join("company");
        ParameterExpression<Integer> co
                = cb.parameter(Integer.class, "companyId");
        predicateList.add(cb.equal(company.get("companyId"), co));
    }

    if (predicateList.size() == 0) {
        query.select(cont);
    } else {
        if (predicateList.size() == 1) {
            query.where(predicateList.get(0));
        } else {
            query.where(cb.and(predicateList.toArray(new Predicate[0])));
        }
    }

    TypedQuery<Contacts> tq = em.createQuery(query);
    if (firstName != null) {
        tq.setParameter("firstName", firstName);
    }
    if (surName != null) {
        tq.setParameter("surName", surName);
    }

    if ((countryid != null) && (countryid != 0)) {
        tq.setParameter("countryid", countryid);
    }

    if((companyId != null) && (companyId != 0)) {
        tq.setParameter("companyId", companyId);
    }

    ListDataModel<Contacts> contactsResultList = new ListDataModel<Contacts>(tq.getResultList());
    return contactsResultList;

}
}

0
投票

看看我的库,内部使用规范。它可能适合你。通过这个库,您的控制器将能够接收如下请求:

curl -X GET \
  'https://myexampledomain.com/persons?
  firstName=Biagio
  &lastName_startsWith=Toz
  &birthDate_gte=19910101
  &country_in=IT,FR,DE
  &address_eq=Via Roma 1,Via Milano/,1,20 West/,34th Street
  &company.name_in=Microsoft,Apple,Google
  &company.employees_between=500,5000'

或:

curl -X POST -H "Content-type: application/json" -d '{   "filter" : {
      "operator": "and", // the first filter must contain a root operator: AND, OR or NOT
      "filters" : [
        {
          "operator": "eq",
          "key": "firstName",
          "value": "Biagio"
        },
        {
          "operator": "or",
          "filters": [
            {
              "operator": "startsWith",
              "key": "lastName",
              "value": "Toz",
              "options": {
                "ignoreCase": true
              }
            },
            {
              "operator": "endsWith",
              "key": "lastName",
              "value": "ZZI",
              "options": {
                "ignoreCase": true,
                "trim" : true
              }
            }
          ]
        },
        {
          "operator": "in",
          "key": "company.name",
          "values": ["Microsoft", "Apple", "Google"]
        },
        {
          "operator": "or",
          "filters": [
            {
              "operator": "gte",
              "key": "birthDate",
              "value": "19910101"
            },
            {
              "operator": "lte",
              "key": "birthDate",
              "value": "20010101"
            }
          ]
        },
        {
          "operator": "between",
          "key" : "company.employees",
          "values": [500, 5000],
          "options": {
            "negate": true
          }
        }
      ]   },   "options": {
    "pageSize": 10,
    "pageOffset": 0,
    "sortKey": "birthDate",
    "sortDesc": false   }    }' 'https://myexampledomain.com/persons'
© www.soinside.com 2019 - 2024. All rights reserved.