Spring-Data-Jpa动态查询(Specification),2024年最新java高级程序员面试宝典pdf

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 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 getStuList() {

return stuList;

}

public void setStuList(List 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 {

}

以上数据访问层接口用于对班级表进行相关的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 {

}

3、定义业务层类

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

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

@SuppressWarnings(“serial”)

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

List stus = stuRepository1.findAll(new Specification(){

@Override

public Predicate toPredicate(Root 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 stus = stuRepository1.findAll(new Specification() {

@Override

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

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

List 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 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 getStusByPage(String clazzName, int pageIndex, int pageSize){

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

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

//Specification动态查询

Specification spec = buildSpec(clazzName, pageIndex, pageSize);

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

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

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

return pages;

}

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

Specification spec = new Specification() {

@Override

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

root.join(“clazz”, JoinType.INNER);

Path 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 clazzes){

clazzRepository1.saveAll(clazzes);

}

@Transactional

public void saveStuAll(List stu){

stuRepository1.saveAll(stu);

}

/**

  • 根据性别查询学生信息

  • @param sex

  • @return

*/

@SuppressWarnings(“serial”)

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

List stus = stuRepository1.findAll(new Specification(){

@Override

public Predicate toPredicate(Root 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 stus = stuRepository1.findAll(new Specification() {

@Override

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

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

List 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 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 getStusByPage(String clazzName, int pageIndex, int pageSize){

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

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

//Specification动态查询

Specification spec = buildSpec(clazzName, pageIndex, pageSize);

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

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

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

return pages;

}

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

Specification spec = new Specification() {

@Override

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

root.join(“clazz”, JoinType.INNER);

Path 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;

}

public void setPageSize(int pageSize) {

this.pageSize = pageSize;

}

public int getPageNum() {

return pageNum;

}

public void setPageNum(int pageNum) {

this.pageNum = pageNum;

}

public List<Map<String, Object>> getStuDates() {

return stuDates;

}

public void setStuDates(List<Map<String, Object>> stuDates) {

this.stuDates = stuDates;

}

}

5、定义控制器类

在controller包下新建一个StuController1类,代码如下(访问方法)。

import com.mcy.springdatajpa.custom.PageData;

import com.mcy.springdatajpa.entity.Clazz;

import com.mcy.springdatajpa.entity.Stu;

import com.mcy.springdatajpa.service.SchoolService1;

import org.springframework.data.domain.Page;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

@RestController

@RequestMapping(“/stu1”)

public class StuController1 {

//注入SchoolService1

@Resource

private SchoolService1 schoolService1;

//保存,初始化数据

@RequestMapping(“/save”)

public String save(){

Clazz clazz1 = new Clazz(“软件工程1班”);

Clazz clazz2 = new Clazz(“软件工程2班”);

//保存班级对象数据

List clazzs = new ArrayList<>();

clazzs.add(clazz1);

clazzs.add(clazz2);

schoolService1.saveClazzAll(clazzs);

Stu stu1 = new Stu(“张三”, “湖北”, 20, ‘男’, clazz1 );

Stu stu2 = new Stu(“李四”, “湖北”, 18, ‘女’, clazz1 );

Stu stu3 = new Stu(“诸葛亮”, “湖北”, 19, ‘女’, clazz1 );

Stu stu4 = new Stu(“刘备”, “湖北”, 21, ‘男’, clazz2 );

Stu stu5 = new Stu(“张飞”, “湖北”, 32, ‘女’, clazz2 );

Stu stu6 = new Stu(“关于”, “湖北”, 17, ‘男’, clazz2 );

List stus = new ArrayList<>();

stus.add(stu1);

stus.add(stu2);

stus.add(stu3);

stus.add(stu4);

stus.add(stu5);

stus.add(stu6);

schoolService1.saveStuAll(stus);

return “保存学生对象成功”;

}

@RequestMapping(“/getStusBySex”)

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

return schoolService1.getStusBySex(sex);

}

//动态查询学生信息

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

@RequestMapping(“/getStusByDynamic”)

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

return schoolService1.getStusByDynamic(stu);

}

//分页查询摸个班级的学生信息

@RequestMapping(“/getStusBypage”)

PageData getStusByPage(String clazzName, int pageIndex, int pageSize){

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

Page page = schoolService1.getStusByPage(clazzName, pageIndex, pageSize);

//对查询出来的结果数据进行分析

List stus = page.getContent();

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

for(Stu s: stus){

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

stuMap.put(“name”, s.getName());

stuMap.put(“id”, s.getId());

stuMap.put(“age”, s.getAge());

stuMap.put(“sex”, s.getSex());

stuMap.put(“address”, s.getAddress());

stuMap.put(“clazzName”, clazzName);

stuDatas.add(stuMap);

}

//将分页查询出的结果数据进行分析

//然后把数据存入PageData对象中响应给浏览器展示

PageData data = new PageData();

data.setStuDates(stuDatas);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值