mybatis-plus自动设置records
1、bug描述
以前做分页查询时,我都是直接将page
对象扔到sql
查询的方法中,mybatis-plus
会自动给page
进行赋值。然而在这次问题中,返回的total
结果中有数据,但是recodes
却没有数据
2、bug复现
伪代码如下:
service
层代码
1 | public Page<User> queryList() { |
mapper
层代码
1 | List<User> queryList(; Page<User> page) |
xml
查询语句
1 | <select id="queryList" resultType="com.example.entity.User"> |
最后可以看到page
对象的数据:
3、定位问题
既然以前是可以在查询sql
时给page
进行赋值recodes
的,那我们就追一下源码,看mybatis-plus
是在哪里发起的sql
追源码时,建议将源码下载来,idea中可以直接下,例如:
不然的话,打断点执行时会有偏差,或者运行不到断点
当我们进入到发sql的断点时,点击进入方法内
点几次进入方法内后,我们会进入到com.baomidou.mybatisplus.core.override
包下的MybatisMapperMethod#execute
方法,这里就是执行SQL
的地方
很明显这里分了增删改查,我们的查询,所以这里直接在查询那打断点,直接进入断点
可以看到,这里是根据method.returnsXXX
来判断执行的哪个方法,我们分别看一下里面的方法。当看到result = executeForIPage(sqlSession, args);
时:
1 | private <E> Object executeForIPage(SqlSession sqlSession, Object[] args) { |
这里面有很明显的setRecords
方法,而result
其实就是args
传参的page
对象。所以这大概率就是要找的自动设置records
地方了,而其他的方法都没有这个
所以,如何进入这个方法呢?
上面说过,根据method.returnsXXX
来判断执行的哪个方法,而从判断来看,可以怀疑这是根据返回值的类型来判断的。这时候的method
为:
可以看到reurnType
是和查询的sql
返回值类型是一样的。因此,要进入executeForIPage
的那个方法,只需要将sql
的返回值改成IPage
对象或者实现类就行了
4、解决问题
将sql
的返回对象改Page
对象,再次请求,发现recodes
被自动赋值进去了
1 | Page<User> queryList(; Page<User> page) |
这样问题解决
以前之所以将
sql
的返回值设置为List
,是为了方便其他查询的复用,那时只需要将page
传入null
即可,没想到引发了这样的问题。其实这个bug还有其他的解决方法,就是在查询到数据
list
后,返回page
对象前手动page.setRecords(list)
5、其他拓展
其实之前还好奇过mybatis-plus
的total
等参数,给sql
的分页等是在哪里设置的,这次就一起看看在哪吧。
和上面的语句一样,把sql
的返回值改成page
对象,进入到MybatisMapperMethod#executeForIPage
,断点打到sqlSession.selectList(command.getName(), param)
方法上
我们点进sqlSession.selectList
方法,发现是一个接口,直接在接口方法打断点,运行时会带我们到对应的实现类上
同样的方法点进this.sqlSessionProxy.selectList(statement, parameter)
,运行几次后,就到了DefaultSqlSession#selectList
方法
然后在executor.query(ms, wrapCollection(parameter), rowBounds, handler)
这行打断点,点击进入方法内部
然后就到了Plugin#invoke
,可以看到,interceptor
就是mybatis-plus
的拦截器
打断点进入后,这里就是进行page
设置total
等,还有分页的地方了
具体的total
查询,就在query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)
里
可以看到,如果查询结果的false
的话,就直接返回空的集合,连原先的查询语句都不执行了。进入willDoQuery
,可以看到查询total
的语句
回到MybatisPlusInterceptor#intercept
,进行分页的地方就在查询total
的下面那行代码
该方法会进入PaginationInnerInterceptor#beforeQuery
方法,调试下去会发现有使用page.offset()
和page.getSize()
的地方,就是在这里进行分页
总结下来,其实就是mybatis-plus
实现了mybatis
的Interceptor
拦截器,在里面进行查询total
和将sql
进行分页