写点什么

Spring-Data-Jpa 动态查询(Specification)

  • 2022 年 4 月 23 日
  • 本文字数:6379 字

    阅读完需:约 21 分钟

private String address; ? ? //地址


private Integer age; ? ?//年龄


private char sex; ? //性别


//@JsonIgnore 注解是类注解,作用是 json 序列化时将 java bean 中的一些属性忽略掉


@JsonIgnore


private Clazz clazz;


/**


  • 使用 @id 指定主键。使用代码 @GeneratedValue

  • 指定主键的生存策略,mysql 默认为自动增长


*/


@Id


@GeneratedValue


public Integer getId() {


return id;


}


public void setId(Integer id) {


this.id = id;


}


public String getName() {


return name;


}


public void setName(String name) {


this.name = name;


}


public String getAddress() {


return address;


}


public void setAddress(String address) {


this.address = address;


}


public Integer getAge() {


return age;


}


public void setAge(Integer age) {


this.age = age;


}


public char getSex() {


return sex;


}


public void setSex(char sex) {


this.sex = sex;


}


//关联表对应关系,学生与班级的多对一的关系


@ManyToOne


@CreatedBy


public Clazz getClazz() {


return clazz;


}


public void setClazz(Clazz clazz) {


this.clazz = clazz;


}


public Stu() {


}


public Stu(String name, String address, Integer age, char sex, Clazz clazz) {


this.name = name;


this.address = address;


this.age = age;


this.sex = sex;


this.clazz = clazz;


}


}


Class.java 类(Clazz 和 Stu 是一对多关系,表关联注解代码中有注释)


import com.fasterxml.jackson.annotation.JsonIgnore;


import javax.persistence.*;


import java.util.ArrayList;


import java.util.List;


//用于标记持久化类,SpringBoot 项目加载后会自动根据持久化类建表


@Entity


//设置表名为 tb_clazz


@Table(name="tb_clazz")


public class Clazz {


private Integer id; //主键


private String name; ? ?//班级名称


//@JsonIgnore 注解是类注解,作用是 json 序列化时将 java bean 中的一些属性忽略掉


@JsonIgnore


private List<Stu> stuList = new ArrayList<>();


/**


  • 使用 @id 指定主键。使用代码 @GeneratedValue(strategy = GenerationType.IDENTITY)

  • 指定主键的生存策略,mysql 默认为自动增长


*/


@Id


@GeneratedValue


public Integer getId() {


return id;


}


public void setId(Integer id) {


this.id = id;


}


public String getName() {


return name;


}


public void setName(String name) {


this.name = name;


}


//班级与学生是一对多的关联,mappedBy 对应 Stu 表中 clazz 字段


@OneToMany(cascade=CascadeType.ALL,mappedBy="clazz")


public List<Stu> getStuList() {


return stuList;


}


public void setStuList(List<Stu> stuList) {


this.stuList = stuList;


}


public Clazz(String name) {


this.name = name;


}


public Clazz() {


}


}


从上述代码可以看出,班级与学生是一对多的关系,一个班级可以有多名学生,此处做的是双向关联,即在班级对象中关联了学生对象,在学生对象中也关联了班级对象。

2、定义数据访问层接口

之后在 repository 包下新建一个接口,命名为 StuRepository1,该接口继承 JpaRepository 接口,以持久化对象 Stu 作为 JpaRepository 的第一个类型参数,表示当前所操作的持久化对象类型,Integer 作为 JpaRepository 的第二个类型参数,用于指定 ID 类型,同时创建一个接口名称为 ClazzRepository1,继承 JpaRepository 的接口,用于访问班级信息的数据。完整代码如下。


ClazzRepository1.java 接口


import com.mcy.springdatajpa.entity.Clazz;


import org.springframework.data.jpa.repository.JpaRepository;


import org.springframework.data.jpa.repository.JpaSpecificationExecutor;


public interface ClazzRepository1 extends JpaRepository<Clazz, Integer>, JpaSpecificationExecutor<Clazz> {


}


以上数据访问层接口用于对班级表进行相关的 CRUD 操作,同时由于实现了 JpaSpecificationExecutor 接口,ClazzRepository 接口也将拥有 JpaSpecificationExecutor 接口提供的功能。


StuRepository1.java 接口


import com.mcy.springdatajpa.entity.Stu;


import org.springframework.data.jpa.repository.JpaRepository;


import org.springframework.data.jpa.repository.JpaSpecificationExecutor;


public interface StuRepository1 extends JpaRepository<Stu, Integer>, JpaSpecificationExecutor<Stu> {


}

3、定义业务层类

在 service 包下新建一个 SchoolService1.java 类,其中主要的几个查询方法有。


根据性别查询学生信息方法。


@SuppressWarnings("serial")


public List<Map<String, Object>> getStusBySex(char sex){


List<Stu> stus = stuRepository1.findAll(new Specification<Stu>(){


@Override


public Predicate toPredicate(Root<Stu> root, CriteriaQuery<?> query, CriteriaBuilder cb) {


//root.get("sex")表示获取 sex 这个字段名称,equal 表示执行 equal 查询


//相当于 select s from Stu s where s.sex = ?1


Predicate p1 = cb.equal(root.get("sex"), sex);


return p1;


}


});


List<Map<String, Object>> results = new ArrayList<>();


//遍历查询出的学生对象,提取姓名,年龄,性别信息


for(Stu s: stus){


Map<String, Object> stu = new HashMap<>();


stu.put("name", s.getName());


stu.put("age", s.getAge());


stu.put("sex", s.getSex());


results.add(stu);


}


return results;


}


动态查询学生信息:可以根据学生对象的姓名(模糊匹配),地址查询(模糊匹配),性别,班级查询学生信息,如果没有传入参数,默认查询所有的学生信息。


@SuppressWarnings("serial")


public List<Map<String, Object>> getStusByDynamic(Stu stu){


List<Stu> stus = stuRepository1.findAll(new Specification<Stu>() {


@Override


public Predicate toPredicate(Root<Stu> root, CriteriaQuery<?> query, CriteriaBuilder cb) {


//本集合用于封装查询条件


List<Predicate> predicates = new ArrayList<>();


if(stu != null){


//是否传入用于查询的姓名


if(!StringUtils.isEmpty(stu.getName())){


predicates.add(cb.like(root.get("name"), "%"+stu.getName()+"%"));


}


//判断是否传入查询的地址


if(!StringUtils.isEmpty(stu.getAddress())){


predicates.add(cb.like(root.get(" 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 address"), "%"+stu.getAddress()+"%"));


}


//判断是否传入查询的性别


if(stu.getSex() != '\0'){


predicates.add(cb.equal(root.get("sex"), stu.getSex()));


}


//判断是否传入用于查询的班级信息


if(stu.getClazz() != null && !StringUtils.isEmpty(stu.getClazz().getName())){


root.join("clazz", JoinType.INNER);


Path<String> clazzName = root.get("clazz").get("name");


predicates.add(cb.equal(clazzName, stu.getClazz().getName()));


}


}


return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();


}


});


List<Map<String, Object>> results = new ArrayList<>();


//遍历查询出的学生对象,提取姓名,年龄,性别信息


for(Stu s : stus){


Map<String, Object> stuMap = new HashMap<>();


stuMap.put("name", stu.getName());


stuMap.put("age", stu.getAge());


stuMap.put("sex", stu.getSex());


stuMap.put("address", stu.getAddress());


stuMap.put("clazzName", stu.getClazz().getName());


results.add(stuMap);


}


return results;


}


分页查询某个班级的学生信息 @param clazzName 代表班级名称,@param pageIndex 代表当前查询第几页 ,@param pageSize 代表每页查询的最大数据量。


@SuppressWarnings("serial")


public Page<Stu> getStusByPage(String clazzName, int pageIndex, int pageSize){


//指定排序参数对象:根据 id,进行降序查询


Sort sort = Sort.by(Sort.Direction.DESC, "id");


//Specification 动态查询


Specification<Stu> spec = buildSpec(clazzName, pageIndex, pageSize);


//分页查询学生信息,返回分页实体对象数据


//pages 对象中包含了查询出来的数据信息以及与分页相关的信息


Page<Stu> pages = stuRepository1.findAll(spec, PageRequest.of(pageIndex-1, pageSize, sort));


return pages;


}


private Specification<Stu> buildSpec(String clazzName, int pageIndex, int pageSize) {


Specification<Stu> spec = new Specification<Stu>() {


@Override


public Predicate toPredicate(Root<Stu> root, CriteriaQuery<?> query, CriteriaBuilder cb) {


root.join("clazz", JoinType.INNER);


Path<String> cn = root.get("clazz").get("name");


Predicate p1 = cb.equal(cn, clazzName);


return p1;


}


};


return spec;


}


SchoolService1.java 中全部代码。


import com.mcy.springdatajpa.entity.Clazz;


import com.mcy.springdatajpa.entity.Stu;


import com.mcy.springdatajpa.repository.ClazzRepository1;


import com.mcy.springdatajpa.repository.StuRepository1;


import org.springframework.data.domain.Page;


import org.springframework.data.domain.PageRequest;


import org.springframework.data.domain.Sort;


import org.springframework.data.jpa.domain.Specification;


import org.springframework.stereotype.Service;


import org.springframework.transaction.annotation.Transactional;


import org.springframework.util.StringUtils;


import javax.annotation.Resource;


import javax.persistence.criteria.*;


import java.util.ArrayList;


import java.util.HashMap;


import java.util.List;


import java.util.Map;


@Service


public class SchoolService1 {


//注入数据访问层接口对象


@Resource


private StuRepository1 stuRepository1;


@Resource


private ClazzRepository1 clazzRepository1;


@Transactional


public void saveClazzAll(List<Clazz> clazzes){


clazzRepository1.saveAll(clazzes);


}


@Transactional


public void saveStuAll(List<Stu> stu){


stuRepository1.saveAll(stu);


}


/**


  • 根据性别查询学生信息

  • @param sex

  • @return


*/


@SuppressWarnings("serial")


public List<Map<String, Object>> getStusBySex(char sex){


List<Stu> stus = stuRepository1.findAll(new Specification<Stu>(){


@Override


public Predicate toPredicate(Root<Stu> root, CriteriaQuery<?> query, CriteriaBuilder cb) {


//root.get("sex")表示获取 sex 这个字段名称,equal 表示执行 equal 查询


//相当于 select s from Stu s where s.sex = ?1


Predicate p1 = cb.equal(root.get("sex"), sex);


return p1;


}


});


List<Map<String, Object>> results = new ArrayList<>();


//遍历查询出的学生对象,提取姓名,年龄,性别信息


for(Stu s: stus){


Map<String, Object> stu = new HashMap<>();


stu.put("name", s.getName());


stu.put("age", s.getAge());


stu.put("sex", s.getSex());


results.add(stu);


}


return results;


}


/**


  • 动态查询学生信息:可以根据学生对象的姓名(模糊匹配),地址查询(模糊匹配),性别,班级查询学生信息

  • 如果没有传入参数,默认查询所有的学生信息

  • @param stu

  • @return


*/


@SuppressWarnings("serial")


public List<Map<String, Object>> getStusByDynamic(Stu stu){


List<Stu> stus = stuRepository1.findAll(new Specification<Stu>() {


@Override


public Predicate toPredicate(Root<Stu> root, CriteriaQuery<?> query, CriteriaBuilder cb) {


//本集合用于封装查询条件


List<Predicate> predicates = new ArrayList<>();


if(stu != null){


//是否传入用于查询的姓名


if(!StringUtils.isEmpty(stu.getName())){


predicates.add(cb.like(root.get("name"), "%"+stu.getName()+"%"));


}


//判断是否传入查询的地址


if(!StringUtils.isEmpty(stu.getAddress())){


predicates.add(cb.like(root.get("address"), "%"+stu.getAddress()+"%"));


}


//判断是否传入查询的性别


if(stu.getSex() != '\0'){


predicates.add(cb.equal(root.get("sex"), stu.getSex()));


}


//判断是否传入用于查询的班级信息


if(stu.getClazz() != null && !StringUtils.isEmpty(stu.getClazz().getName())){


root.join("clazz", JoinType.INNER);


Path<String> clazzName = root.get("clazz").get("name");


predicates.add(cb.equal(clazzName, stu.getClazz().getName()));


}


}


return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();


}


});


List<Map<String, Object>> results = new ArrayList<>();


//遍历查询出的学生对象,提取姓名,年龄,性别信息


for(Stu s : stus){


Map<String, Object> stuMap = new HashMap<>();


stuMap.put("name", s.getName());


stuMap.put("age", s.getAge());


stuMap.put("sex", s.getSex());


stuMap.put("address", s.getAddress());


stuMap.put("clazzName", s.getClazz().getName());


results.add(stuMap);


}


return results;


}


/***


  • 分页查询某个班级的学生信息

  • @param clazzName 代表班级名称

  • @param pageIndex 代表当前查询第几页

  • @param pageSize 代表每页查询的最大数据量

  • @return


*/


@SuppressWarnings("serial")


public Page<Stu> getStusByPage(String clazzName, int pageIndex, int pageSize){


//指定排序参数对象:根据 id,进行降序查询


Sort sort = Sort.by(Sort.Direction.DESC, "id");


//Specification 动态查询


Specification<Stu> spec = buildSpec(clazzName, pageIndex, pageSize);


//分页查询学生信息,返回分页实体对象数据


//pages 对象中包含了查询出来的数据信息以及与分页相关的信息


Page<Stu> pages = stuRepository1.findAll(spec, PageRequest.of(pageIndex-1, pageSize, sort));


return pages;


}


private Specification<Stu> buildSpec(String clazzName, int pageIndex, int pageSize) {


Specification<Stu> spec = new Specification<Stu>() {


@Override


public Predicate toPredicate(Root<Stu> root, CriteriaQuery<?> query, CriteriaBuilder cb) {


root.join("clazz", JoinType.INNER);


Path<String> cn = root.get("clazz").get("name");


Predicate p1 = cb.equal(cn, clazzName);


return p1;


}


};


return spec;


}


}


在业务层中需要注入数据访问层对象,在上述代码中我们是通过 @Resource 注解将 StuRepository 接口以及 ClazzRepository 接口对应的实现类对象注入的,同时在业务层方法中定义三个方法,分别实现了对班级信息的条件查询,动态 SQL 语句以及分页查询。

4、定义分页的页面数据对象

在项目下新建一个包,命名为 custom,在 custom 下新建一个 java 类,命名为 PageData.java,此类用于封装分页查询出的数据信息,主要包含了当前页码(pageIndex)、满足查询条件下用于分页的数据总量(totalCount)、当前条件下总共可以分的总页数(pageSize)、当前页码展示的数据量(pageNum)以及查询出的数据信息(stuDatas)。详细代码如下。


import java.util.ArrayList;


import java.util.List;


import java.util.Map;


/**


  • 定义一个对象用于封装一页的数据


*/


public class PageData {


//定义一个变量用于存放当前页码


private int pageIndex;


//定义一个变量用于保存满足查询条件下用于分页的数据总量


private long totalCount;


//定义一个变量用于保存当前条件下可以分页的总页数


private int pageSize;


//定义一个变量用于保存当前页码查询出的数据总量


private int pageNum;


//定义一个变量用于保存当前查询出来的学生信息


private List<Map<String, Object>> stuDates = new ArrayList<>();


public int getPageIndex() {


return pageIndex;


}


public void setPageIndex(int pageIndex) {


this.pageIndex = pageIndex;


}


public long getTotalCount() {


return totalCount;


}


public void setTotalCount(long totalCount) {


this.totalCount = totalCount;


}


public int getPageSize() {


return pageSize;


}

用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
Spring-Data-Jpa动态查询(Specification)_Java_爱好编程进阶_InfoQ写作社区