Spring Boot 与 MyBatis-Plus 常见问题:分页、批量更新与性能优化
欢迎阅读我的文章!更多精彩内容,欢迎关注:
• B站主页:小枫Geek
• 微信公众号:Procode
在 Spring Boot 项目中,MyBatis-Plus(简称 MP)几乎是最常见的 ORM 框架之一。 它不仅简化了 CRUD,还内置了分页插件、批量操作、逻辑删除、乐观锁等功能,大幅降低了开发成本。
不过在实际开发中,很多同学还是会遇到一些常见问题:
-
分页插件分页不生效、总数统计太慢;
-
批量更新写起来麻烦,性能不佳;
-
大表查询性能差,优化无从下手。
本文就来结合实际场景,系统梳理 Spring Boot + MyBatis-Plus 的几个高频问题和解决方案。
1、分页问题
1. 分页插件没生效
常见现象:
-
代码里写了分页,但结果还是全量返回;
-
Page对象总是空,total为 0;
原因分析:
-
MyBatis-Plus 3.4 之后需要显式注册分页插件;
-
旧版
PaginationInterceptor已废弃,应该用MybatisPlusInterceptor。
正确写法:
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
分页查询示例:
Page<User> page = new Page<>(1, 10); // 第1页, 每页10条
Page<User> result = userMapper.selectPage(page, new QueryWrapper<User>().eq("status", 1));
System.out.println("总数:" + result.getTotal());
System.out.println("数据:" + result.getRecords());
2. 分页 count 查询太慢
当表数据量过千万时,分页插件默认会执行:
SELECT COUNT(*) FROM user WHERE status = 1;
大表统计会拖垮性能。
优化方案:
-
手动控制 count 查询:
Page<User> page = new Page<>(1, 10, false); // 不查总数
-
条件优化:建合适索引,让 count 更快。
-
缓存总数:对于变化不频繁的表,可以用 Redis 缓存 count 值。
-
搜索类分页:如果只是“加载更多”,可以用游标分页(基于 ID)。
2、批量更新问题
1. 常见写法性能低
很多人用 for 循环一个个 updateById,导致 SQL 执行多次:
for(User user : userList) {
userMapper.updateById(user);
}
这种写法对数据库压力大,效率低。
2. 推荐的批量更新方式
方式一:MP 提供的 updateBatchByIdService 层已经内置了批量更新方法:
boolean flag = userService.updateBatchById(userList, 1000);
第二个参数 1000 表示分批大小,避免 SQL 太长。
方式二:自定义 SQL + foreach如果需要根据条件批量更新,可以在 Mapper 里写 XML:
<update id="batchUpdateStatus">
update user
set status = #{status}
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
调用:
userMapper.batchUpdateStatus(1, Arrays.asList(1,2,3,4));
这种方式能一次性更新多条数据,效率远高于单条 update。
3、性能优化问题
1. 大表分页优化
-
问题:
limit offset分页在 offset 很大时性能极差。 -
优化方案: 改用 ID 游标分页。
SELECT * FROM user WHERE id > #{lastId} ORDER BY id ASC LIMIT 10;
比传统的 limit 100000, 10 快得多。
2. 查询列过多
-
问题:
select *会返回所有字段,带宽和内存浪费。 -
优化方案: 只查必要字段。
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "username", "email").eq("status", 1);
List<User> users = userMapper.selectList(wrapper);
3. N+1 查询问题
-
问题: 查询用户列表后,再为每个用户查订单,导致大量 SQL。
-
优化方案: 用一次性 in 查询 + 关联组装。
List<Order> orders = orderMapper.selectList(
new QueryWrapper<Order>().in("user_id", userIds)
);
然后在 Java 内存里做关联,避免重复 SQL。
4. 批量插入优化
MP 的 saveBatch 默认也是分批 insert,提高效率:
userService.saveBatch(userList, 1000);
如果数据量极大,可以考虑 MyBatis-Plus 扩展库 mybatis-plus-join 或者直接用 INSERT INTO ... VALUES ... 多值插入。
4、总结
-
分页
-
确保注册分页插件;
-
大表分页尽量用游标分页;
-
count 查询可缓存或优化索引。
-
-
批量更新
-
避免 for 循环逐条更新;
-
优先用
updateBatchById或自定义 foreach。
-
-
性能优化
-
少用
select *,明确字段; -
合理设计索引;
-
避免 N+1 查询,多用 in 查询批量获取。
-
MyBatis-Plus 已经解决了大部分通用问题,但在实际业务中:
-
如果你遇到复杂多表查询,可能需要自己写 SQL,而不是依赖 MP 提供的 CRUD;
-
在超大规模数据(亿级别)场景下,分页和批量操作还需要结合 分库分表、读写分离 来优化;
-
在微服务架构中,建议结合分页 API 标准化,避免不同团队使用不同分页参数。
更多推荐

所有评论(0)