mybatis-plus分頁插件之count優(yōu)化

示例代碼
分頁插件配置
package com.example.demo.conf;

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

@Configuration
@MapperScan(basePackages = "com.example.demo.mapper")
public class MybatisPlusConfig {

    //配置分頁插件注入容器
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}
分頁代碼示例
分頁查詢control

    @PostMapping("/selectPage")
    public IPage<SplitExperimentType> selectAll(){
        Page page = new Page();
        page.setCurrent(1L);
        page.setSize(2L);
        IPage<SplitExperimentType> list = service.selectAll(page);
        return list;
    }
分頁查詢service

    @Override
    public IPage<SplitExperimentType> selectAll(Page page) {
        return baseMapper.selectAll(page);
    }
分頁查詢mapper

IPage<SplitExperimentType> selectAll(IPage page);
分頁查詢mapper.xml

<select id="selectAll" resultType="com.example.demo.model.SplitExperimentType">
    select * from split_experiment_type
</select>


優(yōu)化前分頁sql執(zhí)行流程
1,先執(zhí)行count統(tǒng)計,統(tǒng)計sql如下,規(guī)則時方法sql結(jié)果作為子查詢,在外套一層select count(*) from (select * from split_experiment_type);

2, count > 0 ,在執(zhí)行方法映射 sql

第一步:select count(*) from (select * from split_experiment_type);

第二步:select * from split_experiment_type limit xxx


性能分析
select count(*) from (select * from split_experiment_type)

這個sql肯定有問題,select * from split_experiment_type 會掃描全列,即使用明確列替換 * 也是會掃描在統(tǒng)計count是不需要的列;

期望是在count統(tǒng)計數(shù),最優(yōu)解是只統(tǒng)計符合條件的2級索引,或者主鍵索引,只統(tǒng)計索引列,性能剛剛的

但是統(tǒng)計count是sql是自動生成的,有什么辦法不使用mybatis-plus的分頁插件自動生成的count的sql呢,當然可以,下面我們看下mybatis-plus的分頁插件部分源碼

package com.baomidou.mybatisplus.extension.plugins.inner;


public class PaginationInnerInterceptor implements InnerInterceptor {
   
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        //獲取page對象
        IPage<?> page = (IPage)ParameterUtils.findPage(parameter).orElse((Object)null);
        
        if (page != null && page.getSize() >= 0L && page.searchCount()) {
            //根據(jù)page對象的里的countId來查詢是否配置了自定義count的方法,countId就是自定義統(tǒng)計sql的mapper里的方法名
            MappedStatement countMs = this.buildCountMappedStatement(ms, page.countId());
            BoundSql countSql;
            //如根據(jù)countId能獲取MappedStatement,即存在自定義的方法執(zhí)行,就執(zhí)行自定義的mapper方法來統(tǒng)計count
            if (countMs != null) {
                
                countSql = countMs.getBoundSql(parameter);
            }
            //如根據(jù)countId不能獲取MappedStatement,就自己組裝count統(tǒng)計sql
            else {
                countMs = this.buildAutoCountMappedStatement(ms);
                String countSqlStr = this.autoCountSql(page.optimizeCountSql(), boundSql.getSql());
                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 = 0L;
            if (CollectionUtils.isNotEmpty(result)) {
                Object o = result.get(0);
                if (o != null) {
                    total = Long.parseLong(o.toString());
                }
            }

            page.setTotal(total);
            return this.continuePage(page);
        } else {
            return true;
        }
    }

  ......
   
}



所以,可以看出如果我們在page對象中配置的countId(mapper里自定義統(tǒng)計count的方法名),并且存在mapper,存在countId對象值得統(tǒng)計方法(返回值是Long),并且在xml中進行了實現(xiàn),就會走自定義的統(tǒng)計方法

優(yōu)化后分頁查詢代碼
controller的查詢方法不變



service里

 @Override
    public IPage<SplitExperimentType> selectAll(Page page) {
        //指定自定義統(tǒng)計sql的方法名
        page.setCountId("selectAll_COUNT");
        return baseMapper.selectAll(page);
    }


mapper里新增一個selectAll_COUNT的方法

IPage<SplitExperimentType> selectAll(IPage page);
Long selectAll_COUNT(IPage page);
mapper.xml新增一個selectAll_COUNT方法的實現(xiàn)

<select id="selectAll" resultType="com.example.demo.model.SplitExperimentType">
        select * from split_experiment_type
</select>

<select id="selectAll_COUNT" resultType="java.lang.Long">
        SELECT COUNT(1) FROM split_experiment_type
</select>
優(yōu)化后分頁查詢sql執(zhí)行流程
第一步:SELECT COUNT(1) FROM split_experiment_type

第二步:select * from split_experiment_type limit xxx
count的統(tǒng)計可以通過自己想法進行優(yōu)化,避免不必要的性能開支

特此記錄,希望能幫到大家



作者: IT學習道場


歡迎關(guān)注微信公眾號 : IT學習道場