Markdown|SpringBoot MyBatis-Plus 多表联查分页实战

举报
JiHua 发表于 2026/10/19 10:38:26 2026/10/19
【摘要】 MyBatis-Plus 简化了单表CRUD开发,但很多新手在多表关联分页查询时容易踩坑。本文结合实战场景,讲解不写复杂XML、使用MP分页插件实现多表联查分页,附带完整代码、配置和测试案例,同时对比几种多表查询方案的优缺点,适合后端开发快速落地业务列表查询功能

前言

MyBatis-Plus(简称 MP)作为国内常用的MyBatis增强工具,极大减少了单表增删改查的样板代码。但日常业务中,几乎所有列表接口都需要多表关联查询,例如订单表关联用户表、商品表,同时还要支持分页、条件筛选。

很多同学有两个误区:

  1. 认为MP只能做单表,多表必须手写大量XML;
  2. 直接使用MP分页插件嵌套join语句,忽略分页插件原理,导致总数统计异常。

本文采用自定义Mapper方法 + MP分页插件的方式实现多表分页,兼顾开发效率与性能,代码可直接复制到项目运行。

环境依赖

SpringBoot 2.7.x + MyBatis-Plus 3.5.3.1 + MySQL8.0

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

场景说明

业务场景:订单列表查询,需要查询订单主表t_order,关联用户表t_user获取用户名,支持分页、订单号模糊查询。

  • t_order:id, order_no, user_id, amount, create_time
  • t_user:id, username, phone

1. 分页插件配置(必须配置)

MP分页插件是实现分页的核心,不配置会导致分页参数不生效,total总数为0

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // MySQL分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

2. 实体类

订单实体

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@TableName("t_order")
public class Order {
    private Long id;
    private String orderNo;
    private Long userId;
    private Integer amount;
    private LocalDateTime createTime;
}

用户实体

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@Data
@TableName("t_user")
public class User {
    private Long id;
    private String username;
    private String phone;
}

3. 自定义返回DTO(多表查询返回对象)

不要直接使用数据库实体返回,使用DTO接收多表查询字段,符合分层开发规范

import lombok.Data;
import java.time.LocalDateTime;

@Data
public class OrderUserDTO {
    private Long id;
    private String orderNo;
    private Long userId;
    private String username;
    private Integer amount;
    private LocalDateTime createTime;
}

4. Mapper接口

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import java.util.Map;

public interface OrderMapper extends com.baomidou.mybatisplus.core.mapper.BaseMapper<Order> {
    // 多表联查分页
    IPage<OrderUserDTO> selectOrderWithUserPage(Page<OrderUserDTO> page, @Param("query") Map<String,Object> query);
}

5. Mapper.xml 编写SQL

resources/mapper/OrderMapper.xml

<?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](http://mybatis.org/dtd/mybatis-3-mapper.dtd)">
<mapper namespace="com.demo.mapper.OrderMapper">
    <select id="selectOrderWithUserPage" resultType="com.demo.dto.OrderUserDTO">
        SELECT o.id, o.order_no, o.user_id, u.username, o.amount, o.create_time
        FROM t_order o
        LEFT JOIN t_user u ON o.user_id = u.id
        <where>
            <if test="query.orderNo != null">
                o.order_no LIKE CONCAT('%',#{query.orderNo},'%')
            </if>
        </where>
        ORDER BY o.create_time DESC
    </select>
</mapper>

重点:这里只需要写主体查询SQL,分页插件会自动拼接limit,自动执行count查询,无需手动写count语句。

6. Service层调用示例

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@Service
public class OrderService {

    @Resource
    private OrderMapper orderMapper;

    public IPage<OrderUserDTO> getOrderPage(long pageNum, long pageSize, String orderNo){
        Page<OrderUserDTO> page = new Page<>(pageNum, pageSize);
        Map<String,Object> query = new HashMap<>();
        query.put("orderNo", orderNo);
        return orderMapper.selectOrderWithUserPage(page, query);
    }
}

7. Controller测试接口

import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Resource
    private OrderService orderService;

    @GetMapping("/page")
    public IPage<OrderUserDTO> page(
            @RequestParam(defaultValue = "1") long pageNum,
            @RequestParam(defaultValue = "10") long pageSize,
            @RequestParam(required = false) String orderNo
    ){
        return orderService.getOrderPage(pageNum, pageSize, orderNo);
    }
}

8. 踩坑点总结

  1. 分页插件一定要注册Bean,否则分页不生效;
  2. 多表联查尽量使用LEFT JOIN,避免主表数据丢失;
  3. 多表查询返回不要用Entity,推荐DTO;
  4. 复杂SQL不要强行用MP的Wrapper,自定义XML可读性更好;
  5. 大数据量场景,count查询会有性能问题,可以自定义count语句优化。

9. 方案对比

方案 优点 缺点 适用场景
MP Wrapper 单表 + 循环查关联数据 不用写XML N+1查询问题,性能差 小数据量简单关联
自定义XML + MP分页插件 性能可控,灵活 少量XML 绝大多数业务列表
Join + 手写分页SQL 完全可控 需要手动维护分页、count 超大数据量

结语

MyBatis-Plus 不是替代MyBatis,而是增强工具。单表CRUD交给MP简化开发,多表复杂查询交给原生MyBatis XML,二者结合才是最佳实践。本文的多表分页方案在企业项目中广泛使用,上手简单,同时规避N+1等常见性能坑。实际开发中还可以结合参数校验、接口返回统一封装,进一步完善接口。


【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。