【问题标题】:JPA Criteria API with multiple parameters具有多个参数的 JPA 标准 API
【发布时间】:2012-08-25 07:58:36
【问题描述】:

我需要创建一个使用带有多个参数的 JPA Criteria API 的搜索方法。 现在的问题是并非每个参数都是必需的。所以有些可能是空的,它们不应该包含在查询中。我已经用 CriteriaBuilder 尝试过这个,但我不知道如何让它工作。

使用 Hibernate Criteria API,这相当容易。只需创建条件,然后添加限制。

Criteria criteria = session.createCriteria(someClass.class);
if(someClass.getName() != null) {
   criteria.add(Restrictions.like("name", someClass.getName());
}

如何使用 JPA 的 Criteria API 实现相同的目标?

【问题讨论】:

    标签: java jpa criteria-api


    【解决方案1】:

    看看这个网站JPA Criteria API。例子很多。

    更新:提供具体示例

    让我们搜索余额低于特定值的帐户:

    SELECT a FROM Account a WHERE a.balance < :value
    

    首先创建一个 Criteria Builder

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery<Account> accountQuery = builder.createQuery(Account.class);
    Root<Account> accountRoot = accountQuery.from(Account.class);
    ParameterExpression<Double> value = builder.parameter(Double.class);
    accountQuery.select(accountRoot).where(builder.lt(accountRoot.get("balance"), value));
    

    要获取结果集参数并运行查询:

    TypedQuery<Account> query = entityManager.createQuery(accountQuery);
    query.setParameter(value, 1234.5);
    List<Account> results = query.getResultList();
    

    顺便说一句:entityManager 被注入到 EJB/Service/DAO 的某个地方。

    【讨论】:

    • 如果我不设置参数,这将如何反映在查询中?因为我仍然需要将其他参数附加到 CriteriaQuery。如果其中一个为 null,则会导致意外结果。
    【解决方案2】:

    概念是构造javax.persistence.Predicate 的数组,其中只包含我们想要使用的谓词:

    要查询的示例实体:

    @Entity
    public class A {
        @Id private Long id;    
        String someAttribute;
        String someOtherAttribute;
        ...
    }
    

    查询本身:

        //some parameters to your method
        String param1 = "1";
        String paramNull = null;
    
        CriteriaBuilder qb = em.getCriteriaBuilder();
        CriteriaQuery cq = qb.createQuery();
        Root<A> customer = cq.from(A.class);
    
        //Constructing list of parameters
        List<Predicate> predicates = new ArrayList<Predicate>();
    
        //Adding predicates in case of parameter not being null
        if (param1 != null) {
            predicates.add(
                    qb.equal(customer.get("someAttribute"), param1));
        }
        if (paramNull != null) {
            predicates.add(
                    qb.equal(customer.get("someOtherAttribute"), paramNull));
        }
        //query itself
        cq.select(customer)
                .where(predicates.toArray(new Predicate[]{}));
        //execute query and do something with result
        em.createQuery(cq).getResultList();
    

    【讨论】:

    • 如何获得em 实例?
    • @Alex em,也就是 EntityManager,可能只是自动插入。您的配置类会设置它。
    【解决方案3】:

    Mikko 的回答效果很好。我唯一需要做的改变就是替换:

    cq.select(customer).where(predicates.toArray(new Predicate[]{}));

    与:

    Predicate [] predicatesarr = predicates.toArray(new Predicate[predicates.size()]); 
    cq.select(customer).where(predicatesarr);
    

    原版中从列表到数组的转换在某些地方不起作用。

    【讨论】:

    • 谢谢,空数组和 array.size() 都对我有用。知道有什么区别吗?
    【解决方案4】:

    首先,Mikko 的回答让我得到了答案。对此点赞。

    我的场景是我想要父母/孩子的关系,我想在 ~any~ 孩子上找到一个匹配项。

    员工有多个职位。

    我想找一个员工(那里有很多职称),但在我发送的任何职称上都可以找到它。

    SQL 看起来像:

    Select * from dbo.Employee e join dbo.JobTitle jt on e.EmployeeKey = jt.EmployeeKey 在哪里(jt.JobTitleName = 'programmer' 或 jt.JobTitleName = 'badcop')

    我输入了性别和出生日期来完成示例(并提供更多“可选”)标准)

    我的 JPA 代码

    import org.apache.commons.lang3.StringUtils;
    import org.springframework.data.jpa.domain.Specification;
    
    import javax.persistence.criteria.CriteriaBuilder;
    import javax.persistence.criteria.CriteriaQuery;
    import javax.persistence.criteria.Join;
    import javax.persistence.criteria.Predicate;
    import javax.persistence.criteria.Root;
    import java.util.ArrayList;
    import java.util.List;
    
    public class MyEmployeeSpecification implements Specification<MyEmployee> {
    
        private MyEmployee filter;
    
        public MyEmployeeSpecification(MyEmployee filter) {
            super();
            this.filter = filter;
        }
    
        public Predicate toPredicate(Root<MyEmployee> root, CriteriaQuery<?> cq,
                                     CriteriaBuilder cb) {
    
            Predicate returnPred = cb.disjunction();
    
            List<Predicate> patientLevelPredicates = new ArrayList<Predicate>();
    
            if (filter.getBirthDate() != null) {
                patientLevelPredicates.add(
                        cb.equal(root.get("birthDate"), filter.getBirthDate()));
            }
    
            if (filter.getBirthDate() != null) {
                patientLevelPredicates.add(
                        cb.equal(root.get("gender"), filter.getGender()));
            }
    
            if (null != filter.getJobTitles() && filter.getJobTitles().size() > 0) {
    
                List<Predicate> jobTitleLevelPredicates = new ArrayList<Predicate>();
    
                Join<JobTitle, JobTitle> hnJoin = root.join("jobtitles");
    
                for (JobTitle hnw : filter.getJobTitles()) {
                    if (null != hnw) {
                        if (StringUtils.isNotBlank(hnw.getJobTitleName())) {
                            jobTitleLevelPredicates.add(cb.equal(hnJoin.get("getJobTitleName"), hnw.getFamily()));
                        }
                    }
                }
    
                patientLevelPredicates.add(cb.or(jobTitleLevelPredicates.toArray(new Predicate[]{})));
            }
    
            returnPred = cb.and(patientLevelPredicates.toArray(new Predicate[]{}));
    
            return returnPred;
        }
    }
    

    但由于 predicates.toArray(new Predicate[]{}) ,也就是可变参数技巧,我发现了我的想法。 (感谢 Mikko)

    我也在做“实现规范”的方法。

    其他有用的链接:

    JPA Specifications by Example

    JPA CriteriaBuilder conjunction criteria into a disjunction criteria

    【讨论】:

      【解决方案5】:

      一个简单的 Spring 解决方案,使用 lambda 表达式:

      Specification<User> specification = (root, query, builder) -> {
          List<Predicate> predicates = new ArrayList<>();
      
          // like
          predicates.add(builder.like(root.get("name"), "%test%"));
      
          // equal
          predicates.add(builder.equal(root.get("parent_id"), 99L);
      
      
          // AND all predicates
          return builder.and(predicates.toArray(new Predicate[0]));
      };
      
      repository.findAll(specification);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-21
        • 2018-08-22
        • 2018-09-06
        • 2017-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多