共计 3350 个字符,预计需要花费 9 分钟才能阅读完成。
网上基本上都是举荐配置如下:
@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。