SpringDataJPA之JpaSpecificationExecutor接口的使用
JpaSpecificationExecutor:该接口不能单独使用,如上图所示该接口并未直接或间接继承 Repository所以该接口不是 Repository的实现接口,没有对应的操作数据库的方法,必须和以上三个接口组合使用,主要用非主键查询,以及一些别的查询。
JpaSpecificationExecutor接口是独立于前面几个几口的一个工具接口,因此不能单独使用,下面结合JpaRepository接口进行方法的实现。
由于导包,创建POJO,applicationContext.xml,数据库文件和前面的Respositiry的相同就不再贴代码了,直接上它的接口和测试类。
- 创建接口实现继承
package com.OVA.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import com.OVA.pojo.Users;
/**
* @author OVA_Wom
* JpaSpecificationExecutor
* 该接口不能单独使用,需要配合JPA中的其他的接口使用,用于非主键条件的查询,主要处理多条件查询和排序分页处理
* 原因:JpaSpecificationExecutor该接口没有继承Respository这个JPA标记接口,Spring无法生成对应的代理对象
**/
public interface UserDao extends JpaRepository<Users, Integer>,JpaSpecificationExecutor<Users> {
}
- 测试类编写
package test;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.OVA.dao.UserDao;
import com.OVA.pojo.Users;
/**
* @author OVA_Wom JpaSpecificationExecuto测试接口
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpaSpecificationExecutorTest {
@Autowired
private UserDao userdao;
Users users = null;
// 单条件查询,根据用户姓名查询数据
@Test
public void TestFindByName() {
/*
* 泛型是查询的对象,Specification实际上是一个封装了查询规则的查询对象
* 注意:Specification其实就是对hibernatejpa QBC查询方式的封装,
* 其中包括,CriteriaBuilder,CriteriaQuery
* Root,Predicate对像;由于Specification是一个接口类型
* 所以使用匿名内部类的方式进行实现这个接口,让它回调toPredicate方法
*/
Specification<Users> specification = new Specification<Users>() {
/*
* Predicate对象封装了要查询的条件 root是查询的根对象,对要查询的对象进行一个二次封装,方便去数据库中的各个字段
* CriteriaQuery<?>封装了一个基本的查询 select* from对象名;
* CriteriaBuilder创建查询,创建CriteriaQuery<?>对象
*/
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// 进行姓名的比较
Predicate pre = cb.equal(root.get("username"), "董小姐");
return pre;
}
};
List<Users> findAll = userdao.findAll(specification);
for (Users users : findAll) {
System.out.println(users);
}
}
// 方式一 多条加查询
// 用户姓名和年龄作为查询的条件
@Test
public void TestFindByNameAndAge1() {
Specification<Users> specification = new Specification<Users>() {
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// 设置多个查询条件
List<Predicate> list = new ArrayList<>();
Predicate username = cb.equal(root.get("username"), "董小姐");
// greaterThanOrEqualTo年龄大于等于40
Predicate userage = cb.greaterThanOrEqualTo(root.get("userage"), 40);
list.add(username);
list.add(userage);
/*
* 如果查询条件多与两个就要使用Predicate[]数组方式
* 创建Predicate[]数组,数组长度为list.size()
*/
Predicate[] predicates = new Predicate[list.size()];
// list.toArray将集合里面数据转存到数组里面
Predicate[] array = list.toArray(predicates);
// 该方式需要传入一个数组
Predicate and = cb.and(array);
return and;
}
};
List<Users> findAll = userdao.findAll(specification);
for (Users users : findAll) {
System.out.println(users);
}
}
// 方式二 多条件查询
// 用户姓名或者年龄作为查询的条件
@Test
public void TestFindByNameAndAge2() {
Specification<Users> specification = new Specification<Users>() {
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//查询年龄小于30或者姓名是董小姐的用户
return cb.or(cb.equal(root.get("username"), "董小姐"), cb.lessThanOrEqualTo(root.get("userage"), 30));
}
};
List<Users> findAll = userdao.findAll(specification);
for (Users users : findAll) {
System.out.println(users);
}
}
@Test
//JpaSpecificationExecutor实现排序
//查询姓名以董开头的用户的姓名,并且按照ID降序排序
public void TestFindByNameSort(){
Specification<Users> specification=new Specification<Users>() {
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.like(root.get("username").as(String.class), "董%");
}
};
//排序
Sort sort=new Sort(Direction.DESC,"userid");
List<Users> findAll = userdao.findAll(specification,sort);
for (Users users : findAll) {
System.out.println(users);
}
}
@Test
//JpaSpecificationExecutor实现分页
//查询姓名以董开头的用户的姓名
public void TestFindByNamePage(){
Specification<Users> specification=new Specification<Users>() {
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.like(root.get("username").as(String.class), "董%");
}
};
//分页
//一页显示的条数
int size=2;
//打印第几页下表从0开始
int page=0;
Pageable pageable=new PageRequest(page, size);
Page<Users> findAll = userdao.findAll(specification,pageable);
System.out.println("总页数"+findAll.getTotalPages());
System.out.println("分页总条数"+findAll.getTotalElements());
for (Users users : findAll.getContent()) {
System.out.println(users);
}
}
@Test
//JpaSpecificationExecutor实现查询分页并且排序
//查询姓名以董开头的用户的姓名以ID降序并且分页显示
//由于排序对象被整合到了Pagable对象中所以我们调用PageRequest(page, size, sort)
public void TestFindByNamePageAndSort(){
Sort sort = new Sort(Direction.DESC,"userid");
int size=2;
int page=0;
PageRequest pageRequest = new PageRequest(page, size, sort);
Specification<Users> specification=new Specification<Users>() {
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// TODO Auto-generated method stub
return cb.like(root.get("username"), "董%");
}
};
Page<Users> findAll = userdao.findAll(specification,pageRequest);
System.out.println("总页数"+findAll.getTotalPages());
System.out.println("总条数:"+findAll.getTotalElements());
for (Users users : findAll.getContent()) {
System.out.println(users);
}
}
}
代码已经加详细的注释,以上就是JpaSpecificationExecuto接口的基本内容,不足之处还请大神指正。