【问题标题】:Correct usage of JPA criteria API, Predicates and where method of CriteriaQuery正确使用 JPA 标准 API、谓词和 CriteriaQuery 的 where 方法
【发布时间】:2012-08-28 00:14:43
【问题描述】:

我正在尝试测试 JPA 存储库。这是我的客户端/测试代码:

@Test
    public void testFindBoitesByMultiFieldWithIdentifiantSet() {
        BoiteDataOnDemand dod = new BoiteDataOnDemand();
        Boite boite = dod.getSpecificBoite(0);
        boite.setIdentifiant("theIdentifiant");
        boiteRepository.save(boite);
        assertEquals(10, Boite.countBoites());
        BoiteQueryInfo boiteQueryInfo = new BoiteQueryInfo();
        boiteQueryInfo.setIdentifiant("theIdentifiant");
        List<Boite> boites = boiteRepository.findBoitesByMultiField(boiteQueryInfo, 1, 5, "identifiant", "desc");
        assertEquals(1, boites.size());
    }

这里是存储库方法:

@Override
    public List<Boite> findBoitesByMultiField(BoiteQueryInfo boiteQueryInfo, Integer firstResult_, Integer maxResults_, String sort_, String order_) {
        log.debug("findBoitesByMultiField");

        final String identifiant = boiteQueryInfo.getIdentifiant();
        final Date dateOuvertureFrom = boiteQueryInfo.getDateOuvertureFrom();

        CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
        CriteriaQuery<Boite> c = criteriaBuilder.createQuery(Boite.class);
        Root<Boite> boite = c.from(Boite.class);
        List<Predicate> criteria = new ArrayList<Predicate>();
            ...
        if (identifiant != null && !identifiant.trim().equals("")) {
            ParameterExpression<String> parameter = criteriaBuilder.parameter(String.class, "identifiant");
            Predicate condition = criteriaBuilder.like(boite.<String> get("identifiant"), parameter);
            criteria.add(condition);
        }

        if (dateOuvertureFrom != null && dateOuvertureTo != null) {
            ParameterExpression<Date> parameterDateOuvertureFrom = criteriaBuilder.parameter(Date.class, "dateOuvertureFrom");
            ParameterExpression<Date> parameterDateOuvertureTo = criteriaBuilder.parameter(Date.class, "dateOuvertureTo");
            Predicate condition = criteriaBuilder.between(boite.<Date> get("dateOuverture"), parameterDateOuvertureFrom, parameterDateOuvertureTo);
            criteria.add(condition);
        } else if (dateOuvertureFrom != null) {
            ParameterExpression<Date> parameter = criteriaBuilder.parameter(Date.class, "dateOuvertureFrom");
            Predicate condition = criteriaBuilder.greaterThanOrEqualTo(boite.<Date> get("dateOuverture"), parameter);
            criteria.add(condition);
        } else if (dateOuvertureTo != null) {
            ParameterExpression<Date> parameter = criteriaBuilder.parameter(Date.class, "dateOuvertureTo");
            Predicate condition = criteriaBuilder.lessThanOrEqualTo(boite.<Date> get("dateOuverture"), parameter);
            criteria.add(condition);
        }

            ...    
        if (order.equalsIgnoreCase("desc")) {
            c.orderBy(criteriaBuilder.desc(boite.get(sort)));
        } else {
            c.orderBy(criteriaBuilder.asc(boite.get(sort)));
        }

        for (Predicate predicate : criteria) {
            c.where(criteriaBuilder.and(predicate));
        }

        TypedQuery<Boite> q = em.createQuery(c);

        if (identifiant != null && !identifiant.trim().equals("")) {
            q.setParameter("identifiant", "%" + identifiant + "%");
        }

        if (dateOuvertureFrom != null && dateOuvertureTo != null) {
            q.setParameter("dateOuvertureFrom", dateOuvertureFrom);
            q.setParameter("dateOuvertureTo", dateOuvertureTo);
        } else if (dateOuvertureFrom != null) {
            q.setParameter("dateOuvertureFrom", dateOuvertureFrom);
        } else if (dateOuvertureTo != null) {
            q.setParameter("dateOuvertureTo", dateOuvertureTo);
        }

            ...    
        return q.setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
    }

但是,测试总是在 assertEquals(1, boites.size()); 处失败,表明没有返回任何结果,即 (java.lang.AssertionError: expected:&lt;1&gt; but was:&lt;0&gt;)。

我强烈怀疑这里有问题:

for (Predicate predicate : criteria) {
            c.where(criteriaBuilder.and(predicate));
        }

但我不确定如何“和”标准。

谁能给点建议?

附:仅供参考,BoiteDataOnDemand 将随机 10 行插入到 boite 表中。

编辑:对代码进行了编辑以使其更短。

【问题讨论】:

  • 您发布的代码太长了。请只发布它的相关部分。
  • 我放弃了 JPA 的标准 API。 QueryDSL 及其流畅的 API 更易于理解和简洁。见:queryDSL

标签: hibernate jpa jpa-2.0 criteria-api


【解决方案1】:

从您在帖子最后部分的提示开始,我同意您为 where 子句添加谓词的方式不正确。

我看到了两种处理方式:

第一种方式

使用谓词数组

List<Predicate> predicates = new ArrayList<Predicate>();
for (Key key : keys) {
    predicates.add(criteriaBuilder.equal(root.get(key), value));
}
c.where(criteriaBuilder.and(predicates.toArray(new Predicate[] {})));

第二种方式

在循环中修改同一个谓词

Predicate predicate = criteriaBuilder.conjunction();
for (Key key : keys) {
    Predicate newPredicate = criteriaBuilder.equal(root.get(key), value);
    predicate = criteriaBuilder.and(predicate, newPredicate);
}
c.where(predicate);

已编辑

再次查看您的代码示例后,我发现您已经以正确的方式创建了PredicateS 的列表:您将其命名为criteria。您只是以错误的方式使用它们。请参阅我的第一个示例的最后一行。


编辑 2

为了查看问题是否是由使用PredicateExpressionS 产生的,在您的情况下不是特别需要的,请尝试暂时删除它们。从

修改您的第一个条件
if (identifiant != null && !identifiant.trim().equals("")) {
    ParameterExpression<String> parameter = criteriaBuilder.parameter(String.class, "identifiant");
    Predicate condition = criteriaBuilder.like(boite.<String> get("identifiant"), parameter);
    criteria.add(condition);
}

if (identifiant != null && !identifiant.trim().equals("")) {
    Predicate condition = criteriaBuilder.like(boite.get("identifiant"), "%" + identifiant + "%");
    criteria.add(condition);
}

【讨论】:

  • 感谢您输入 Perissf。你能告诉我value 变量的类型和值吗?问候。
  • 那只是伪代码。您可以改用您的谓词。您可能不会使用循环,但该概念对于您的 if 子句仍然有效。希望清楚。
  • 使用代码的最后一行后我的测试行为是相同的,即c.where(criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()])));还有其他想法吗?
  • 我已按照您在第二次编辑中的建议更改了代码,并注释掉了以下行:q.setParameter("identifiant", "%" + identifiant + "%"); 不幸的是,这没什么区别...
  • 我认为: c.where(criteriaBuilder.and(predicates.toArray(new Predicate[] {}))); 是多余的,因为它可以重写为: c.where(predicates.toArray (新谓词[] {}));
【解决方案2】:

对我有用的完整解决方案如下:

        CriteriaBuilder builder = session.getCriteriaBuilder();
        CriteriaQuery<Object[]> criteriaQuery = builder.createQuery(Object[].class);
        Root<Eje> ejeRoot = criteriaQuery.from(Eje.class);
        Root<FactorCritico> factorRoot = criteriaQuery.from(FactorCritico.class);


        int factorParam = 2;

        criteriaQuery.multiselect(ejeRoot,factorRoot);

        List<Predicate> predicates = new ArrayList<>();

        Predicate ejeJoinFactorCritico = builder.equal(ejeRoot.get("id"), factorRoot.get("eje"));
        predicates.add(ejeJoinFactorCritico);   

        if (factorParam> 0){
            Predicate factorFilter = builder.equal(factorRoot.get("id"), factorParam);
            predicates.add(factorFilter);   
        }

        criteriaQuery.where(builder.and(predicates.toArray(new Predicate[] {})));

        Query<Object[]> query = session.createQuery(criteriaQuery);
        List<Object[]> resultList = query.getResultList();

        for(Object[] objects: resultList){
            Eje eje = (Eje) objects[0];
            System.out.println("eje: "+eje.getName());

            FactorCritico factor= (FactorCritico) objects[1];
            System.out.println("--> factor: "+factor.getName()+ ", eje: "+ factor.getEje().getName());

            //System.out.println("eje, factor size = "+eje.getFactorCriticos());
        }

【讨论】:

  • 大代码块的解释对以后的读者很有帮助
  • 我有一个变量“factorParam”用作标志,如果它的变量大于零,则将 Predicate 对象添加到 Predicate 列表对象,然后将列表谓词添加到 where 语句.
猜你喜欢
  • 2020-08-24
  • 2021-12-07
  • 2015-12-16
  • 2011-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-26
  • 1970-01-01
相关资源
最近更新 更多