- GITHUB同步代码:https://github.com/codedemo-club/jpa-criteria-api-in-expressions
- 原文地址:https://www.baeldung.com/jpa-criteria-api-in-expressions
1. 概述
我们在进行查询时,经常会遇到查询实体的某个字段位于某个范围的情况。
本文我们将介绍如何使用Criteria API来解决此类问题。
2. 示例实体
在正式开始以前,我们先准备如下两个实体:Department以及DeptEmployee,这两个实体间的关系为 1:n 。
DeptEmployee实体如下,该实体中使用@ManyToOne注解声明了多对一的关系。
@Entity public class DeptEmployee { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private long id; private String title; @ManyToOne private Department department; // 以下省略了setter/getter }
Department 实体如下,使用@OneToMany注解声明了一对多的关系:
@Entity public class Department { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private long id; private String name; @OneToMany(mappedBy="department") private List<DeptEmployee> employees; // 以下省略了setter/getter }
3. CriteriaBuilder.In
首先让我们看一下CriteriaBuilder接口。该接口上定义了一个in()方法,此方法接收的参数类型为 Expression,返回值类型为Predicate。将返回的Predicate做为查询条件传入至where()方法中,即可将范围做为某个字段的查询条件:
CriteriaQuery<DeptEmployee> criteriaQuery = criteriaBuilder.createQuery(DeptEmployee.class); Root<DeptEmployee> root = criteriaQuery.from(DeptEmployee.class); In<String> inClause = criteriaBuilder.in(root.get("title")); for (String title : titles) { inClause.value(title); } criteriaQuery.select(root).where(inClause);
在Spring Boot中,你可以如下获取criteriaBuilder(GITHUB示例代码):
@Autowired EntityManager entityManager; ... CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
或者配合仓库层如下使用(GITHUB示例代码):
@Test void spring() { String[] titles = {title}; Specification<DeptEmployee> deptEmployeeSpecification = new Specification<DeptEmployee>() { @Override public Predicate toPredicate(Root<DeptEmployee> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { CriteriaBuilder.In<String> inClause = criteriaBuilder.in(root.get("title")); for (String title : titles) { inClause.value(title); } return inClause; } }; assertEquals(10, this.deptEmployeeCrudRepository.findAll(deptEmployeeSpecification).size()); }
4. Expression.In
另外,还可以使用Expression接口提供的in()方法:
criteriaQuery.select(root) .where(root.get("title") .in(titles));
与CriteriaBuilder.in()不同的是,Expression.in() 接收的参数类型为Collection。对比上述两处代码可以看出Expression.in() 的写法更简单一些。
5. 在子查询中使用IN表达式
接下来让我们看看如何在子查询中使用IN表达式。
比如有如下需求:查询出所有部门名称中包含某个搜索值的所有部门Department的所有员工DeptEmployee:
Subquery<Department> subquery = criteriaQuery.subquery(Department.class); Root<Department> dept = subquery.from(Department.class); subquery.select(dept) .distinct(true) .where(criteriaBuilder.like(dept.get("name"), "%" + departmentName + "%")); criteriaQuery.select(employeeRoot) .where(criteriaBuilder.in(employeeRoot.get("department")).value(subquery));
如上代码:我们创建一个名为subquery的子查询,并将其做为一个表达式传入了value()方法中。
6. 总结
本文中,我们介绍了几种IN查询的方法,在文章的最后对子查询给出了示例。希望对你能有所帮助。