Mybatis Plus带多条件的多表联合、分页、排序查询

本文介绍如何使用SpringBoot和MybatisPlus实现复杂条件下的多表联合查询与分页功能,包括模糊搜索、范围搜索及排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

注意:本程序使用SpringBoot+Mybatis Plus

一、现有表#

student学生表:#

idstuNamestuAgegraduateDatefacultyId
1卢1212019-11-20 20:29:201
2卢2202019-11-27 20:29:402
3卢3222019-11-28 20:29:533
4卢4172019-11-28 20:30:202
5卢5172019-11-21 20:29:201
6卢6172025-12-11 20:29:203
7卢7202019-11-20 20:29:202
8卢8222019-11-27 20:29:403
9卢9172025-12-11 20:29:202
10卢10212019-11-28 20:30:201
11卢11172019-11-21 20:29:201
12卢12172019-11-11 20:29:203
13卢13172019-11-20 20:29:202
14卢14182025-12-11 20:29:203
15卢15222019-11-28 20:29:533
16卢16222019-11-28 20:30:201
17卢17182019-11-21 20:29:201
18卢18202025-12-11 20:29:202
19卢19212019-11-21 20:29:203
20卢20192025-12-11 20:29:203
21卢21182019-11-28 22:16:171

facultylist学院表:#

idfacultyName
1计算机与通信工程学院
2数学与统计学院
3文法学院

二、同时满足以下需求:#

1.多表联合查询出学院名字#

需求展示:学生表联合学院表隐去学院id直接展示学院名给用户:

idstuNamestuAgegraduateDatefacultyName
1卢1212019-11-20 20:29:20计算机与通信工程学院
2卢2202019-11-27 20:29:40数学与统计学院
3卢3222019-11-28 20:29:53文法学院
4卢4172019-11-28 20:30:20数学与统计学院
5卢5172019-11-21 20:29:20计算机与通信工程学院

2.可以带多条件查询#

学生名字模糊搜索#
年龄范围搜索#
搜索是否毕业#
指定字段排序#

3.指定页码,页数据大小进行物理分页查询#

三、解决步骤#

今天找了一下午资料,终于可以同时满足以上所有需求了!开干!

Spring Boot配置#

重要配置已经做注释!

Copyserver.port=9999
#设置jackson的时区和输出形式
spring.jackson.time-zone=GMT+8
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
#URL要设置数据库编码、时区、允许多语句查询(等会会讲原因)
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&serverTimezone=GMT%2b8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=6666
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#关闭字段名的映射(关闭如userName映射成user_name)
mybatis-plus.configuration.map-underscore-to-camel-case=false
#配置xml Mapper路径
mybatis-plus.mapper-locations=classpath:mapping/*Mapper.xml

MP配置#

创建一个MP的配置类写入如下内容,主要是配置mapper路径

Copypackage com.looyeagee.web.util;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement
@Configuration
@MapperScan("com.looyeagee.web.mapper")
public class MybatisPlusConfig {

}

实体类编写#

Student.java#
Copypackage com.looyeagee.web.bean;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Student implements Serializable {
    private Long id;
    private String stuName;
    private Integer stuAge;
    private Date graduateDate;
    private Long facultyId;
}
Select.java(把查询条件封装成类)#
Copypackage com.looyeagee.web.bean;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.Serializable;

@Data
public class Select implements Serializable {
    String stuName;//模糊搜索学生名
    Integer minAge;//最小年龄 用Integer不用int是因为用户可以不选择此条件(null)即没有最小年龄限制 用int默认值是0
    Integer maxAge;//最大年龄
    Boolean isGraduate;//是否毕业 为null就是不管毕业还是没毕业都要
    Integer pageNumber;//第几页 从1开始
    Integer pageSize;//每页几个数据
    String orderBy;//排序字段
    Boolean highToLow;//是否降序 为false和null就是升序 为true就是降序
}
Result.java(把结果也封装)#
Copypackage com.looyeagee.web.bean;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)//为null的字段 不输出到前端 看以下的stuAge字段
public class Result implements Serializable {
    private Long id;
    private String stuName;
    private Integer stuAge;//为什么要用Integer而不是int 因为int会有默认值0,而Integer默认是null,如果查到年龄为null就会显示为0岁了,这样输出到前端就是0岁,而我们不期望前端展示为null的数据
    private Date graduateDate;
    private String facultyName;
}

StudentMapper.xml编写#

前提知识:

mysql如何通过当前页码和页数据条数分页?#

mysql limit m,n是mysql分页用的语句,不过此处的m不是当前页码而是从第几条数据开始,n是取几条数据。所以假设PageNumber为第几页,PageSize为一页数据条数,则写法为limit (PageNumber-1)*PageSize,PageSize

如何用mysql limit分页查询的同时返回符合条件数据总数以确定总页数?#

用上面的方法虽然能分页,但是没有查出符合条件总个数,所以就不知道数据总共有多少页数。MP内部分页的实现是先查询符合条件的个数再用limit查询,不过这样会导致两次查询浪费资源,而且要写2个条件一模一样的select查询,很不方便,这里有个解决方案可以一次性返回数据和总条数:通过SQL_CALC_FOUND_ROWS和SELECT FOUND_ROWS();来获取。
为了说的更清楚,我直接贴出测试sql:我的21个数据中名字含有字符"2"的有4个同学,我想每页展示2条数据,并且访问第1页:根据我贴出的数据,满足条件的有4个,id分别为2,12,20,21。因为1页只展示2个数据,所以第1页应该是返回2,12这两个数据。语句

CopySELECT SQL_CALC_FOUND_ROWS * FROM `student` WHERE stuName LIKE '%2%' limit 0,2;SELECT FOUND_ROWS();

执行后一次性返回2个结果集(这就是前面要配置一次性可执行多个语句的原因,默认不可以一次性执行多个语句),如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mFDb1Vh5-1627262378927)(https://looyeagee.cn/usr/uploads/2019/11/3012192170.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bHgb2Qi2-1627262378929)(https://looyeagee.cn/usr/uploads/2019/11/2437070153.png)]

第二个结果集就是总条数。下面介绍Mapper的配置。

2个结果集的配置:

Copy<resultMap id="ResultMap" type="com.looyeagee.web.bean.Result"/>
<resultMap id="RecordsCount" type="integer"/>

resultMap标签是mapper的子标签用来指定结果集的id和类型。由于返回的第一个结果集的结果为我们定义的实体类Result,所以类型填写完整实体类路径;由于返回的第二个结果集的结果为一个整数,所以类型是integer。

Copy  <select id="findResultByInfo" resultMap="ResultMap,RecordsCount"
            parameterType="com.looyeagee.web.bean.Select" resultType="java.util.List">

在select标签中添加resultMap属性来指定结果集的id,由于这个select会返回2个结果集,所以resultMap属性填写2个刚刚定义的结果集id,用英文逗号隔开。parameterType属性就是我们封装的查询实体类。返回的结果类型是List。

下面贴出完整多条件查询代码:
注意:
1.大于号小于号要用xml实体字符转义。
2.orderby字段的使用要用${},不让程序自动预编译。
3.排序多加一个通过id升序,因为mysql排序是不稳定的,可能会出现不同页数出现相同数据的情况。
4.此处传进去的pageNumber已经经过了PageNumber=(PageNumber-1)*PageSize的处理。

Copy<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.looyeagee.web.mapper.StudentMapper">    <resultMap id="ResultMap" type="com.looyeagee.web.bean.Result"/>    <resultMap id="RecordsCount" type="integer"/>    <select id="findResultByInfo" resultMap="ResultMap,RecordsCount"            parameterType="com.looyeagee.web.bean.Select" resultType="java.util.List">        SELECT SQL_CALC_FOUND_ROWS        `student`.`id` AS `id`,        `student`.`stuName` AS `stuName`,        `student`.`stuAge` AS `stuAge`,        `student`.`graduateDate` AS `graduateDate`,        `facultylist`.`facultyName` AS `facultyName`        FROM        ( `facultylist` JOIN `student` )        WHERE        (        `facultylist`.`id` = `student`.`facultyId`)        -- 标题模糊搜索        <if test="stuName != null">            AND `student`.`stuName` LIKE CONCAT('%',#{stuName},'%')        </if>        -- &gt;=是大于等于        <if test="minAge!=null">            AND `student`.`stuAge`&gt;= #{minAge}        </if>        -- &lt;=是小于等于        <if test="maxAge!=null">            AND `student`.`stuAge` &lt;= #{maxAge}        </if>        -- 没毕业 毕业时间大于现在        <if test="isGraduate  != null and isGraduate ==false">            AND `student`.`graduateDate`&gt;=NOW()        </if>        -- 毕业了 毕业时间小于现在        <if test="isGraduate  != null and isGraduate ==true">            AND `student`.`graduateDate`&lt;=NOW()        </if>        <if test="orderBy!=null and orderBy!=''">            <if test="highToLow ==null or highToLow ==false">                ORDER BY ${orderBy} ASC,`student`.`id` ASC -- 加id ASC是为了保证分页结果的唯一性 mysql排序是不稳定的 https://www.jianshu.com/p/1e8a19738ae4            </if>            <if test="highToLow !=null and highToLow ==true">                ORDER BY ${orderBy} DESC,`student`.`id` ASC            </if>        </if>        -- 分页查询        LIMIT        #{pageNumber},#{pageSize};        -- 接着查询符合条件个数        SELECT FOUND_ROWS();    </select></mapper>

StudentMapper类编写#

注意返回类型即可。

Copypackage com.looyeagee.web.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.looyeagee.web.bean.Select;import com.looyeagee.web.bean.Student;import java.util.List;public interface StudentMapper extends BaseMapper<Student> {    List<List<?>> findResultByInfo(Select select);}

测试类编写#

关于总页数计算的说明:总数据条数除以每页数据条数,如果没有余数,结果就是总页数;如果有余数,则要将计算结果+1(进1法);用一句话就是(totalCount + pageSize - 1) / pageSize

Copypackage com.looyeagee.web;import com.looyeagee.web.bean.Result;import com.looyeagee.web.bean.Select;import com.looyeagee.web.mapper.StudentMapper;import com.looyeagee.web.service.impl.StudentServiceImpl;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTestclass WebApplicationTests {    @Autowired    StudentMapper studentMapper;    @Test    void selectTest() {        Select selectInfo = new Select();        int nowPageIndex = 1;        int pageSize = 5;        selectInfo.setPageNumber((nowPageIndex - 1) * pageSize);        selectInfo.setPageSize(pageSize);        selectInfo.setOrderBy("stuAge");        selectInfo.setHighToLow(true);        selectInfo.setIsGraduate(true);        selectInfo.setMinAge(17);        selectInfo.setMaxAge(20);        List<List<?>> findtest = studentMapper.findResultByInfo(selectInfo);        List<Result> orderinfos = (List<Result>) findtest.get(0);        int totalCount = (Integer) findtest.get(1).get(0);        System.out.println("当前页面记录数:" + orderinfos.size());        System.out.println("符合条件记录数:" + totalCount);        System.out.println("当前页数:" +nowPageIndex);        System.out.println("总页数:" + ((totalCount + pageSize - 1) / pageSize));        orderinfos.forEach(System.out::println);    }}

以上的测试是筛选在我们的21个数据中,最小年龄17,最大年龄20,已经毕业(毕业时间小于现在时间),总共结果是有9个同学:通过年龄降序分别为2,7,17,21,4,5,11,12,13号,
选择第1页,1页5个数据,输出结果

Copy当前页面记录数:5符合条件记录数:9当前页数:1总页数:2Result(id=2, stuName=卢2, stuAge=20, graduateDate=Wed Nov 27 20:29:40 CST 2019, facultyName=数学与统计学院)Result(id=7, stuName=卢7, stuAge=20, graduateDate=Wed Nov 20 20:29:20 CST 2019, facultyName=数学与统计学院)Result(id=17, stuName=卢17, stuAge=18, graduateDate=Thu Nov 21 20:29:20 CST 2019, facultyName=计算机与通信工程学院)Result(id=21, stuName=卢21, stuAge=18, graduateDate=Thu Nov 28 22:16:17 CST 2019, facultyName=计算机与通信工程学院)Result(id=4, stuName=卢4, stuAge=17, graduateDate=Thu Nov 28 20:30:20 CST 2019, facultyName=数学与统计学院)

nowPageIndex改为2,输出第二页结果(剩余4个数据):

Copy当前页面记录数:4符合条件记录数:9当前页数:2总页数:2Result(id=5, stuName=卢5, stuAge=17, graduateDate=Thu Nov 21 20:29:20 CST 2019, facultyName=计算机与通信工程学院)Result(id=11, stuName=卢11, stuAge=17, graduateDate=Thu Nov 21 20:29:20 CST 2019, facultyName=计算机与通信工程学院)Result(id=12, stuName=卢12, stuAge=17, graduateDate=Mon Nov 11 20:29:20 CST 2019, facultyName=文法学院)Result(id=13, stuName=卢13, stuAge=17, graduateDate=Wed Nov 20 20:29:20 CST 2019, facultyName=数学与统计学院)

https://github.com/esofar/cnblogs-theme-silence)

CONTENTS

  1. 一、现有表
  • 1.1. student学生表:
  • 1.2. facultylist学院表:
  1. 二、同时满足以下需求:
  • 2.1. 1.多表联合查询出学院名字
  • 2.2. 2.可以带多条件查询
  • 2.2.1. 学生名字模糊搜索
  • 2.2.2. 年龄范围搜索
  • 2.2.3. 搜索是否毕业
  • 2.2.4. 指定字段排序
  • 2.3. 3.指定页码,页数据大小进行物理分页查询
    三、解决步骤
  • 3.1. Spring Boot配置
  • 3.2. MP配置
  • 3.3. 实体类编写
  • 3.3.1. Student.java
  • 3.3.2. Select.java(把查询条件封装成类)
  • 3.3.3. Result.java(把结果也封装)
  • 3.4. StudentMapper.xml编写
  • 3.4.1. mysql如何通过当前页码和页数据条数分页?
  • 3.4.2. 如何用mysql limit分页查询的同时返回符合条件数据总数以确定总页数?
  • 3.5. StudentMapper类编写
  • 3.6. 测试类编写
Mybatis Plus关联查询分页的实现可以通过使用Mybatis Plus提供的Wrapper类来实现。以下是一个示例: ```java Page<User> page = new Page<>(1, 10); QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("role_id", roleId); wrapper.orderByDesc("create_time"); IPage<User> userPage = userMapper.selectPage(page, wrapper); List<User> userList = userPage.getRecords(); ``` 在以上示例中,我们使用了QueryWrapper来构建查询条件,其中eq示等于,orderByDesc示按照create_time字段进行降序排序。然后我们调用了Mybatis Plus提供的selectPage方法来进行分页查询,其中第一个参数是Page对象,查询分页信息,第二个参数是Wrapper对象,查询的条件。最后我们可以通过getRecords方法获取查询结果。 在关联查询时,我们可以使用Mybatis Plus提供的LambdaQueryWrapper类来进行条件构造,例如: ```java LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(); wrapper.eq(User::getRoleId, roleId) .orderByDesc(User::getCreateTime); IPage<User> userPage = userMapper.selectPage(page, wrapper); ``` 以上示例使用了LambdaQueryWrapper来构造查询条件,其中eq示等于,orderByDesc示按照create_time字段进行降序排序。然后我们调用了Mybatis Plus提供的selectPage方法来进行分页查询,其中第一个参数是Page对象,查询分页信息,第二个参数是Wrapper对象,查询的条件。最后我们可以通过getRecords方法获取查询结果。 需要注意的是,在关联查询时,我们需要使用Mybatis Plus提供的@TableName和@TableField注解来指定名和字段名,以便Mybatis Plus能够正确地生成SQL语句。另外,在查询结果中,我们可以通过使用Mybatis Plus提供的@Result注解来指定实体类中的属性和查询结果中的字段的映射关系。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值