【踩坑篇】MyBatis-Plus拦截 ResultSetHandler.handleResultSets返回结果为空List
文章目录
- 01-描述
- 02-原因
- 03-解决方案
- 04-知识补充 ResultHandler
01-描述
如下图这个代码,返回的 resultList 的 size 为0,导致解密注解未生效,未执行解密逻辑

02-原因
3.5.3.1版本升级成3.5.4.1后解密拦截器有问题,由于两个版本的 BaseMapper#selectOne 方法有变更
-
3.5.3.1版本的BaseMapper#selectOne方法如下
-
3.5.4.1版本的BaseMapper#selectOne方法如下
其主要区别在于两个selectList的区别,重点解释下这个区别
由于 3.5.4.1 版本的 BaseMapper#selectOne 默认传入了一个 ResultHandler,我们可以看一下 ResultSetHandler#handleResultSets 的处理逻辑

再深入看一下 handleResultSet 方法,只有当resultHandler 为空时,会默认创建一个 ResultHandler,并且将查到的结果集加到 multipleResults 里

因此得出结论,由于 3.5.4.1 的 mybatis-plus 给了一个默认的 ResultHandler 导致拦截器得到返回的 List 的 Size 为0
03-解决方案
-
方案一:直接使用
3.5.3.1版本的就可以了🙄,个人觉得这是最好的方案(推荐) -
方案二:把
selectOne方法重写,像下图这样,但是也只能解决 User这一个实体的(治标不治本),不过也够用
04-知识补充 ResultHandler
关于为啥MyBatis-Plus要在 3.5.4.1 版本在 selectOne 方法里默认加一个这个 ResultHandler,如下是ai对于使用 ResultHandler 和不使用的一个总结表格
| 对比维度 | 传统List<T>查询 | ResultHandler流式处理 |
|---|---|---|
| 返回值 | List<T>(直接返回全量结果集合) | void(无返回值,结果通过handleResult方法实时处理) |
| 结果处理方式 | 一次性将所有查询结果转换为 Java 对象,收集到List中,再返回给调用者 | 不收集结果,逐条将ResultSet行数据转换为 Java 对象,直接传递给ResultHandler处理 |
| 内存占用 | 高(与数据量成正比,100 万条数据会生成 100 万个对象并存储在List中,易导致 OOM) | 低(仅保留当前正在处理的单行对象,处理后无引用可被 GC 回收,内存占用稳定) |
| 适用数据量 | 小数据量(通常几千条以内,内存可承载) | 大数据量(上万条及以上,如百万级,避免内存溢出) |
| 数据库连接时间 | 短(= SQL 执行时间 + 一次性读取所有数据到内存的时间,完成后立即释放连接) | 长(= SQL 执行时间 + 所有行的处理时间总和,需保持连接至最后一行处理完毕) |
| 处理时机 | 滞后处理(必须等待全量数据查询完成并返回List后,才能开始遍历处理) | 实时处理(边查询边处理,每获取一行数据就立即处理,无需等待全量结果) |
| 灵活性 | 低(只能通过遍历List处理,逻辑固定) | 高(可在handleResult中自定义任意逻辑,如写入文件、批量同步、实时统计等) |
| 潜在问题 | 数据量过大时易发生内存溢出(OOM) | 连接占用时间长,可能导致连接池资源紧张;需手动管理资源(如关闭流),否则可能资源泄漏 |
| 使用复杂度 | 低(直接调用selectList获取List,遍历即可,无需额外代码) | 较高(需自定义ResultHandler实现类,处理资源释放、异常捕获等细节) |
| 核心依赖 | 依赖List对全量结果的缓存,不依赖ResultSet的流式特性 | 依赖ResultSet的流式游标(next()方法逐条获取数据),避免全量缓存 |
关于对数据库连接时间流程解释,如下表
| 场景 | 数据库连接的生命周期 | 连接持续时间 |
|---|---|---|
传统List<T>查询 | 1. 建立连接 → 2. 执行 SQL → 3. 一次性读取所有数据到List → 4. 关闭ResultSet和Statement → 5. 释放连接(归还连接池) | 短(主要是 “查询 + 一次性读数据” 的时间) |
ResultHandler流式处理 | 1. 建立连接 → 2. 执行 SQL → 3. 逐条读取数据并处理(循环调用next()+handleResult) → 4. 所有行处理完后,关闭ResultSet和Statement → 5. 释放连接 | 长(= 查询时间 + 所有行的处理时间总和) |
