疑问:为什么mybatis-plus可以根据dao中的参数是否包含page对象,给sql拼上对应的limit语句
==ps.这里研究的是最新版本(3.5.11)代码==

1.配置

当执行sql的时候mybatis-plus会对所有SQL语句进行拦截并做各种判断与附加操作(这里只添加分页拦截器),会进入到Mybatis-Plus全局拦截器.

1
2
3
4
5
6
@Bean  
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType return interceptor;
}

2.全局拦截器

主要关注下面这段代码
可以看出,当前sql执行时,被拦截器拦截,发现是查询语句,就会先执行winllDoQuery方法,其次做完在执行 beforeQuery
这两个方法的实现在具体的插件拦截器中,如下面的分页拦截器

1
2
3
4
5
6
for (InnerInterceptor query : interceptors) {  
if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) {
return Collections.emptyList();
}
query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
}

3.分页拦截器

3.1 willDoQuery

先count所有数据,将总数存入分页参数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override  
public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
if (page == null || page.getSize() < 0 || !page.searchCount() || resultHandler != Executor.NO_RESULT_HANDLER) {
return true;
}

BoundSql countSql;
MappedStatement countMs = buildCountMappedStatement(ms, page.countId());
if (countMs != null) {
countSql = countMs.getBoundSql(parameter);
} else {
countMs = buildAutoCountMappedStatement(ms);
String countSqlStr = autoCountSql(page, boundSql.getSql());
PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);
PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());
}

CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);
List<Object> result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql);
long total = 0;
if (CollectionUtils.isNotEmpty(result)) {
// 个别数据库 count 没数据不会返回 0 Object o = result.get(0);
if (o != null) {
total = Long.parseLong(o.toString());
}
}
page.setTotal(total);
return continuePage(page);
}

3.2 beforeQuery

判断数据操作是否有分页参数

1
IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);

处理order拼接

1
2
3
4
5
List<OrderItem> orders = page.orders();  
if (CollectionUtils.isNotEmpty(orders)) {
addOrdered = true;
buildSql = this.concatOrderBy(buildSql, orders);
}

处理分页操作

1
2
3
4
5
6
7
8
9
10
11
12
handlerLimit(page, _limit);  
IDialect dialect = findIDialect(executor);

final Configuration configuration = ms.getConfiguration();
DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());
PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);

List<ParameterMapping> mappings = mpBoundSql.parameterMappings();
Map<String, Object> additionalParameter = mpBoundSql.additionalParameters();
model.consumers(mappings, configuration, additionalParameter);
mpBoundSql.sql(model.getDialectSql());
mpBoundSql.parameterMappings(mappings);

未完待续。。。。