我需要创建一个使用带有多个参数的JPA Criteria API的搜索方法。 现在的问题是不是每个参数都是必需的。所以有些可能为null,它们不应包含在查询中。我已经尝试过使用CriteriaBuilder,但我看不出如何使它工作。
使用Hibernate Criteria API,这非常简单。只需创建条件,然后添加限制。
Criteria criteria = session.createCriteria(someClass.class);
if(someClass.getName() != null) {
criteria.add(Restrictions.like("name", someClass.getName());
}
我如何使用JPA的Criteria API实现相同的目标?
答案 0 :(得分:76)
概念是构造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();
答案 1 :(得分:6)
查看此网站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();
BTW:将entityManager注入EJB / Service / DAO中的某个地方。
答案 2 :(得分:1)
Mikko的回答很精彩。只有改变我需要做的,就是替换:
cq.select(customer).where(predicates.toArray(new Predicate[]{}));
使用:
Predicate [] predicatesarr = predicates.toArray(new Predicate[predicates.size()]);
cq.select(customer).where(predicatesarr);
某处从原始列表到数组的转换不起作用。
答案 3 :(得分:0)
首先,Mikko的回答使我明白了。为此投票。
我的情况是我想与父母/孩子建立关系,我想在〜any〜孩子上找到匹配项。
员工有多个JobTitle。
我想找到一个雇员(其中有很多职位),但是可以在我发送的任何职位上找到它。
SQL看起来像:
从dbo.Employee中选择*在e.EmployeeKey上加入dbo.JobTitle jt = 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 [] {}),也就是varargs的把戏。 (感谢Mikko)
我也在做“实现规范”方法。
其他有用的链接:
JPA CriteriaBuilder conjunction criteria into a disjunction criteria
答案 4 :(得分:0)
使用lambda表达式的Spring的简单解决方案:
Specification<User> specification = (root, query, builder) -> {
List<Predicate> predicates = new ArrayList<>();
if (!Optional.ofNullable(criteria.getName()).orElse("").equals("")) {
// like
predicates.add(builder.like(root.get("name"), "%" + criteria.getName() + "%"));
}
if (Optional.ofNullable(criteria.getParentId()).orElse(0L) != 0) {
// equal
predicates.add(builder.equal(root.get("parent"), criteria.getParentId()));
}
// AND all predicates
return builder.and(predicates.toArray(new Predicate[0]));
};
repository.findAll(specification);