有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。
1.对于JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件。
Specification接口中只定义了如下一个方法:
//构造查询条件 /** * root :Root接口,代表查询的根对象,可以通过root获取实体中的属性 * query :代表一个顶层查询对象,用来自定义查询 * cb :用来构建查询,此对象里有很多条件方法 **/ public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
1.通过单个条件查询对象
/** * 根据条件查询单个对象 */ @Test public void testFindOne() { //匿名内部类 /** * 自定义查询条件 * 1.实现Specification接口(提供泛型:查询的对象类型) * 2.实现toPredicate方法(构造查询条件) * 3.需要借助方法参数中的两个参数( * root:获取需要查询的对象属性 * CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配) * ) * 案例:根据客户名称查询,查询客户名为传智播客的客户 * 查询条件 * 1.查询方式 * cb对象 * 2.比较的属性名称 实体类名称 * root对象 * */ Specification<Customer> spec = new Specification<Customer>() { @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { //1.获取比较的属性 Path<Object> custName = root.get("custName"); //2.构造查询条件 : select * from cst_customer where cust_name = 'hdh' Predicate predicate = cb.equal(custName, "hdh");//进行精准的匹配 (比较的属性,比较的属性的取值) return predicate; } }; Customer customer = customerSpecDao.findOne(spec); System.out.println(customer); }
2.通过多个字段条件查询对象
/** * 多条件查询 * 案例:根据客户名(传智播客)和客户所属行业查询(it教育) * */ @Test public void testSpec1() { /** * root:获取属性 实体类名称 * 客户名 * 所属行业 * cb:构造查询 * 1.构造客户名的精准匹配查询 * 2.构造所属行业的精准匹配查询 * 3.将以上两个查询联系起来 */ Specification<Customer> spec = new Specification<Customer>() { @Override public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Path<Object> p1 = root.get("custName"); Path<Object> p2 = root.get("custIndustry"); Predicate custName = cb.equal(p1, "hdh"); Predicate custIndustry = cb.equal(p2, "it"); Predicate equal = cb.and(custName, custIndustry); return equal; } }; Customer customer = customerSpecDao.findOne(spec); System.out.println(customer); }
3.模糊排序查询
/** * 案例:完成根据客户名称的模糊匹配,返回客户列表 * 客户名称以 ’h‘ 开头 * * equal :直接的到path对象(属性),然后进行比较即可 * gt,lt,ge,le,like : 得到path对象,根据path指定比较的参数类型,再去进行比较 * 指定参数类型:path.as(类型的字节码对象) */ @Test public void testSpec3() { //构造查询条件 Specification<Customer> spec = new Specification<Customer>() { @Override public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) { //查询属性:客户名 Path<Object> custName = root.get("custName"); //查询方式:模糊匹配 Predicate like = cb.like(custName.as(String.class), "h%"); return like; } }; // List<Customer> list = customerDao.findAll(spec); // for (Customer customer : list) { // System.out.println(customer); // } //添加排序 //创建排序对象,需要调用构造方法实例化sort对象 //第一个参数:排序的顺序(倒序,正序) // Sort.Direction.DESC:倒序 // Sort.Direction.ASC : 升序 //第二个参数:排序的属性名称 Sort sort = new Sort(Sort.Direction.DESC,"custId"); List<Customer> list = customerSpecDao.findAll(spec, sort); for (Customer customer : list) { System.out.println(customer); } }
4.分页查询
/** * 分页查询 * Specification: 查询条件 * Pageable:分页参数 * 分页参数:查询的页码,每页查询的条数 * findAll(Specification,Pageable):带有条件的分页 * findAll(Pageable):没有条件的分页 * 返回:Page(springDataJpa为我们封装好的pageBean对象,数据列表,共条数) */ @Test public void testSpec4() { Specification spec = null; //PageRequest对象是Pageable接口的实现类 /** * 创建PageRequest的过程中,需要调用他的构造方法传入两个参数 * 第一个参数:当前查询的页数(从0开始) * 第二个参数:每页查询的数量 */ Pageable pageable = new PageRequest(0,2); //分页查询 Page<Customer> page = customerSpecDao.findAll(null, pageable); System.out.println(page.getContent()); //得到当前页数据集合 System.out.println(page.getTotalElements());//得到总条数 System.out.println(page.getTotalPages());//得到总页数 }