mybatis-plus分页插件失效探究解决
作者:子瞻
网上推荐
网上基本上都是推荐配置如下:
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return mybatisPlusInterceptor; }
但是,仅仅这么做,就能达到我们的预期吗?
分页插件无效原因
其结局就是分页插件没有效果,原因是为什么呢?😔
图1
图2
通过对比上面两张图可以发现,图一DefaultSqlSession.selectList()底层调用Plugin.invoke();图二DefaultSqlSession.selectList()底层调用CachingExecutor.query()。
其中,图一是分页插件生效的调用链,图二是分页插件失效的调用链。
也就是说,分页插件失效的原因是,mybatis-plusPlugin类没有为分页插件拦截器生成Executor代理。
解决方案
具体应该怎么做呢?像下面这样,在构建SqlSessionFactory时,需要在MybatisSqlSessionFactoryBean显示设置Plugin。
@Bean(name = "defaultSqlSessionFactory") public SqlSessionFactory defaultSqlSessionFactory(){ MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean(); bean.setDataSource(dataSource); //设置拦截器 bean.setPlugins(mybatisPlusInterceptor); SqlSessionFactory sqlSessionFactory = bean.getObject(); //设置自动提交 sqlSessionFactory.openSession(true); return sqlSessionFactory; }
那么,为分页插件生成代理类是在什么时机生成呢?先公布答案:
//设置自动提交 sqlSessionFactory.openSession(true);
调用链如下
图3
咱再看细节:DefaultSqlSessionFactory.openSessionFromDataSource()详情:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //这步很关键,创建执行者实例 final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
Configuration.newExecutor()详情:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } //对上面的executor进行代理(目的是把插件和执行器封装为代理类) executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
MybatisPlusInterceptor.pluginAll();
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
通过上面的重点code展示,我们大致了解了部分重要节点中分页插件代理类生成的逻辑。接下来我们继续了解具体分页插件工作的效果。
图4
public boolean willDoQuery(){ if (countMs != null) { countSql = countMs.getBoundSql(parameter); } else { countMs = buildAutoCountMappedStatement(ms); //生成查询count SQL String countSqlStr = autoCountSql(page, boundSql.getSql()); PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql); //构建BoundSql countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter); PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters()); } //查询 count 数值 List<Object> result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql); }
接下来,PaginationInnerInterceptor.beforeQuery()生成分页sql;
最终MybatisPlusInterceptor.intercept()里面的executor.query()执行分页sql。
以上就是mybatis-plus分页插件失效探究解决的详细内容,更多关于mybatis plus分页插件失效的资料请关注脚本之家其它相关文章!