JPASpecificationExecutor接口是单独存在的,它主要是提供了多条件查询的支持(类似Hibernated 的Criteria的查询方式),并且可以在查询中添加排序与分页。
JpaSpecificationExecutor接口的使用一般要和Repository体系的接口一起使用。Dao对象其实是注入了一个代理对象,通过继承Repository体系接口完成代理对象SimpleJpaRepository实现类对象的注入。而JpaSpecificationExecutor对象不在Repository接口体系内那么就无法完成对象注入,所以要使用JpaSpecificationExecutor对象必须要实现Repository体系接口
JpaSpecificationExecutor主要提供了多条件查询的支持,并且可以在查询中添加分页和排序。
示例: 准备工作:实体类:
@Entity
@Table(name = "tb_dept")
public class Dept {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer deptno;
@Column
private String dname;
@Column
private String loc;
//……getter/setter、默认构造方法、全参构造方法
}
第一步:创建Maven项目,添加依赖:
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-test
test
junit
junit
4.12
test
第二步:修改application.yml:
spring:
datasource:
url: jdbc:mysql://localhost:3306/db_test?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF8&autoReconnect=true&failOverReadOnly=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
enable_lazy_load_no_trans: true
show-sql: true
open-in-view: false
第三步:提供测试代码:
测试代码框架
@RunWith(SpringRunner.class)
@SpringBootTest
@EnableAutoConfiguration
public class DeptDaoTest {
@Autowired
private DeptDao deptDao;
}
单条件查询
@Test
public void fun() {
List depts = deptDao.findAll(new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.equal(root.get("dname"),"sales");
}
});
depts.forEach(System.out::println);
}
其中:
- Specification接口:封装了查询信息
- Predicate:封装了单个查询条件
- Root:对查询对象属性的封装,比如我们这里是查询Dept,因此root可以看成是Dept,可以通过此对象获取实体对象字段从而进行条件的设置
- CriteriaQuery:定义一个基本的查询
- CriteriaBuilder:查询条件的构造器
- 实现一:
@Test
public void fun2() {
Specification spc = (root, criteriaQuery, criteriaBuilder) -> {
Predicate p1 = criteriaBuilder.like(root.get("dname"), "%A%");
Predicate p2 = criteriaBuilder.like(root.get("loc"), "%S%");
// return criteriaBuilder.and(new Predicate[]{p1, p2});
return criteriaBuilder.and(p1,p2); //两个return效果一样
};
List depts = deptDao.findAll(spc);
depts.forEach(System.out::println);
}
- 实现二:
@Test
public void fun3(){
Specification spc = (root, criteriaQuery, criteriaBuilder)->criteriaBuilder.and(criteriaBuilder.equal(root.get("dname"),"RESEARCH"),criteriaBuilder.equal(root.get("loc"),"DALLAS"));
List depts= deptDao.findAll(spc);
depts.forEach(System.out::println);
}
- 实现三:
@Test
public void fun4() {
//第一个Specification定义了两个or的equal组合
Specification s1 = (root, criteriaQuery, criteriaBuilder) -> {
Predicate p1 = criteriaBuilder.equal(root.get("deptno"), "20");
Predicate p2 = criteriaBuilder.equal(root.get("deptno"), "30");
return criteriaBuilder.or(p1, p2);
};
//第二个Specification定义了两个or的like组合
Specification s2 = (root, criteriaQuery, criteriaBuilder) -> {
Predicate p1 = criteriaBuilder.like(root.get("dname"), "%S%");
Predicate p2 = criteriaBuilder.like(root.get("loc"), "AS%");
return criteriaBuilder.or(p1, p2);
};
//通过Specification将两个Specification连接起来,第一个条件加where,第二个是and
List depts = deptDao.findAll(Specification.where(s1).and(s2));
depts.forEach(System.out::println);
}
带条件的分页查询
@Test
public void fun5(){
Specification spc = (root, criteriaQuery, criteriaBuilder)->criteriaBuilder.ge(root.get("deptno"),2);
Page page = deptDao.findAll(spc, PageRequest.of(2,3)); //查询第1页
page.getContent().stream().forEach(System.out::println);
}
排序
@Test
public void fun6(){
Specification spc = (root, criteriaQuery, criteriaBuilder)->criteriaBuilder.ge(root.get("deptno"),2);
List depts = deptDao.findAll(spc, Sort.by(Sort.Direction.DESC,"loc"));
depts.stream().forEach(System.out::println);
}
分页+条件+排序
@Test
public void test7(){
Specification spc = (root, criteriaQuery, criteriaBuilder)->criteriaBuilder.ge(root.get("deptno"),2);
Page page = deptDao.findAll(spc, PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "loc")));
page.getContent().stream().forEach(System.out::println);
}