SpringBoot集成PageHelper时出现“在系统中发现了多个分页插件,请检查系统配置!”
近日在项目中使用SpringBoot集成PageHelper后,跑单元测试时出现了“在系统中发现了多个分页插件,请检查系统配置!”这个问题。
如下图所示:
-
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
-
### Error querying database. Cause: java.lang.RuntimeException: 在系统中发现了多个分页插件,请检查系统配置!
-
### Cause: java.lang.RuntimeException: 在系统中发现了多个分页插件,请检查系统配置!
-
-
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
-
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
-
at com.sun.proxy.$Proxy109.selectList(Unknown Source)
-
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)
-
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:137)
-
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:75)
-
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
-
at com.sun.proxy.$Proxy110.selectAll(Unknown Source)
-
at com.lianjia.cto.ke.broadband.service.StationInfoService.selectPage(StationInfoService.java:27)
-
at com.lianjia.cto.ke.broadband.service.StationInfoService$$FastClassBySpringCGLIB$$ee2f34a4.invoke(<generated>)
-
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
-
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
-
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
-
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
-
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
-
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
-
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
-
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
-
at com.lianjia.cto.ke.broadband.service.StationInfoService$$EnhancerBySpringCGLIB$$1da3a0f3.selectPage(<generated>)
-
at com.lianjia.cto.ke.broadband.service.StationInfoServiceTest.selectPage(StationInfoServiceTest.java:27)
-
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
-
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
-
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
-
at java.lang.reflect.Method.invoke(Method.java:498)
-
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
-
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
-
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
-
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
-
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
-
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
-
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
-
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
-
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
-
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
-
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
-
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
-
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
-
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
-
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
-
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
-
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
-
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
-
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
-
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
-
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
-
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
-
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
-
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
-
Caused by: org.apache.ibatis.exceptions.PersistenceException:
-
### Error querying database. Cause: java.lang.RuntimeException: 在系统中发现了多个分页插件,请检查系统配置!
-
### Cause: java.lang.RuntimeException: 在系统中发现了多个分页插件,请检查系统配置!
-
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
-
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:150)
-
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
-
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
-
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
-
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
-
at java.lang.reflect.Method.invoke(Method.java:498)
-
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)
-
... 46 more
-
Caused by: java.lang.RuntimeException: 在系统中发现了多个分页插件,请检查系统配置!
-
at com.github.pagehelper.PageHelper.skip(PageHelper.java:55)
-
at com.github.pagehelper.PageInterceptor.intercept(PageInterceptor.java:92)
-
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)
-
at com.sun.proxy.$Proxy123.query(Unknown Source)
-
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
-
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
-
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
-
at java.lang.reflect.Method.invoke(Method.java:498)
-
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:63)
-
at com.sun.proxy.$Proxy123.query(Unknown Source)
-
at com.github.pagehelper.PageInterceptor.executeAutoCount(PageInterceptor.java:201)
-
at com.github.pagehelper.PageInterceptor.intercept(PageInterceptor.java:113)
-
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)
-
at com.sun.proxy.$Proxy123.query(Unknown Source)
-
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)
-
... 52 more
先上结果:maven的pagehelper-spring-boot-starter这个依赖,提供了自动配置分页插件的功能,所以有两种方法解决这个问题
第一种,SpringBoot启动类的注解上排除这个自动配置
@SpringBootApplication(exclude = PageHelperAutoConfiguration.class)
第二种,在构建SqlSessionFactory时,不要再去手动添加分页的拦截器,在application.yml中进行配置pagehelper的属性,在SpringBoot启动时会将配置自动注入生成拦截器
-
# PageHelper配置
-
pagehelper:
-
offsetAsPageNum: true
-
rowBoundsWithCount: true
-
reasonable: true
-
returnPageInfo: true
-
params: count=countSql
以下是解决问题的思路:
看到这个报错后,查看了一下配置,发现并没有配置多个PageHelper。于是查看了一下报错部分的源码,问题出现在一个skip方法中,如下图所示:
-
public class PageHelper extends PageMethod implements Dialect {
-
//...
-
-
@Override
-
public boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
-
//此处的MSUtils.COUNT为一个常量值"_COUNT"
-
if(ms.getId().endsWith(MSUtils.COUNT)){
-
throw new RuntimeException("在系统中发现了多个分页插件,请检查系统配置!");
-
}
-
//...
-
}
-
-
//...
-
}
继续向上找,找到了分页插件的拦截器
-
public class PageInterceptor implements Interceptor {
-
//...
-
private String countSuffix = "_COUNT";
-
-
@Override
-
public Object intercept(Invocation invocation) throws Throwable {
-
try {
-
//...
-
-
//调用方法判断是否需要进行分页,如果不需要,直接返回结果
-
if (!dialect.skip(ms, parameter, rowBounds)) {
-
//反射获取动态参数
-
String msId = ms.getId();
-
Configuration configuration = ms.getConfiguration();
-
Map<String, Object> additionalParameters = (Map<String, Object>) additionalParametersField.get(boundSql);
-
//判断是否需要进行 count 查询
-
if (dialect.beforeCount(ms, parameter, rowBounds)) {
-
String countMsId = msId + countSuffix;
-
Long count;
-
//先判断是否存在手写的 count 查询
-
MappedStatement countMs = getExistedMappedStatement(configuration, countMsId);
-
if(countMs != null){
-
count = executeManualCount(executor, countMs, parameter, boundSql, resultHandler);
-
} else {
-
countMs = msCountMap.get(countMsId);
-
//自动创建
-
if (countMs == null) {
-
//根据当前的 ms 创建一个返回值为 Long 类型的 ms
-
countMs = MSUtils.newCountMappedStatement(ms, countMsId);
-
msCountMap.put(countMsId, countMs);
-
}
-
count = executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler);
-
}
-
//处理查询总数
-
//返回 true 时继续分页查询,false 时直接返回
-
if (!dialect.afterCount(count, parameter, rowBounds)) {
-
//当查询总数为 0 时,直接返回空的结果
-
return dialect.afterPage(new ArrayList(), parameter, rowBounds);
-
}
-
}
-
//判断是否需要进行分页查询
-
if (dialect.beforePage(ms, parameter, rowBounds)) {
-
//生成分页的缓存 key
-
CacheKey pageKey = cacheKey;
-
//处理参数对象
-
parameter = dialect.processParameterObject(ms, parameter, boundSql, pageKey);
-
//调用方言获取分页 sql
-
String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
-
BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
-
//设置动态参数
-
for (String key : additionalParameters.keySet()) {
-
pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
-
}
-
//执行分页查询
-
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
-
} else {
-
//不执行分页的情况下,也不执行内存分页
-
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
-
}
-
} else {
-
//rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
-
resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
-
}
-
return dialect.afterPage(resultList, parameter, rowBounds);
-
} finally {
-
dialect.afterAll();
-
}
-
}
-
}
可以发现以_COUNT结尾的MappedStatement.id是由分页插件的拦截器自动生成的,而这个由分页插件的生成之后,怎么会又一次的调用skip方法呢?所以猜测是有多个分页插件的拦截器影响到了。于是查阅了一下相关的资料,发现PageHelper引入了SpringBoot的自动配置,以下是自动配置的源码:
-
@Configuration
-
@ConditionalOnBean(SqlSessionFactory.class)
-
@EnableConfigurationProperties(PageHelperProperties.class)
-
@AutoConfigureAfter(MybatisAutoConfiguration.class)
-
public class PageHelperAutoConfiguration {
-
-
@Autowired
-
private List<SqlSessionFactory> sqlSessionFactoryList;
-
-
@Autowired
-
private PageHelperProperties properties;
-
-
/**
-
* 接受分页插件额外的属性
-
*
-
* @return
-
*/
-
@Bean
-
@ConfigurationProperties(prefix = PageHelperProperties.PAGEHELPER_PREFIX)
-
public Properties pageHelperProperties() {
-
return new Properties();
-
}
-
-
@PostConstruct
-
public void addPageInterceptor() {
-
PageInterceptor interceptor = new PageInterceptor();
-
Properties properties = new Properties();
-
//先把一般方式配置的属性放进去
-
properties.putAll(pageHelperProperties());
-
//在把特殊配置放进去,由于close-conn 利用上面方式时,属性名就是 close-conn 而不是 closeConn,所以需要额外的一步
-
properties.putAll(this.properties.getProperties());
-
interceptor.setProperties(properties);
-
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
-
sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
-
}
-
}
-
-
}
至此,问题算是比较明白了,是因为多个分页拦截器的作用,导致了该异常的出现,所以全局只要保留一个就可以了。
SpringBoot集成PageHelper时出现“在系统中发现了多个分页插件,请检查系统配置!”的更多相关文章
- Linux系统中ElasticSearch搜索引擎安装配置Head插件
近几篇ElasticSearch系列: 1.阿里云服务器Linux系统安装配置ElasticSearch搜索引擎 2.Linux系统中ElasticSearch搜索引擎安装配置Head插件 3.Ela ...
- win7或win2008系统中,出现【已停止工作,联机检查解决方案并关闭该程序,关闭程序】解决方法!
win7或win2008系统中,出现[已停止工作,联机检查解决方案并关闭该程序,关闭程序]解决方法! 经过摸索,点击[控制面板]-[操作中心]-[更改操作中心设置]-[问题报告设置]-[从不检查解决方 ...
- springboot集成PageHelper,支持springboot2.0以上版本
第一步:pom文件还是需要引入依赖 <!--mybatis的分页插件--> <dependency> <groupId>com.github.pagehelper& ...
- 关于SpringBoot集成myBatis时,mapper接口注入失败的问题
问题描述: 在Spring Boot集成myBatis时,发现启动时,mapper接口一直注入失败. 现象如下: VehicleDAO就是需要的mapper对象,一个简单的接口. 已经在applica ...
- springboot集成pagehelper插件
1.在pom.xml中引入依赖 <dependency> <groupId>com.github.pagehelper</groupId> <artifact ...
- C#开发BIMFACE系列39 网页集成开发3:审图系统中三维模型比对
系列目录 [已更新最新开发文章,点击查看详细] 在建筑施工图审查系统中,设计单位提交设计完成的模型/图纸,审查专家审查模型/图纸.审查过程中如果发现不符合规范的地方,则流程退回到设计单位,设计 ...
- C#开发BIMFACE系列38 网页集成开发2:审图系统中的模型或图纸批注
系列目录 [已更新最新开发文章,点击查看详细] 在运维或协同的场景中,经常需要对模型或图纸进行批注,及时记录已发现的问题并交给相关负责的人员. 在开始实现功能之前,先了解一下BIMFACE中有 ...
- 用WIN7系统IIS的提示:数据库连接出错,请检查Conn.asp文件中的数据库参数设置
我用科讯的从4.0开始,去年开始很少用科讯做新站了,今天拿来做一下,结果悲剧了,数据库路径老是不对,百度一番又一番的,,最后终于给度娘解决了.分享出来给遇到同样的问题的人. 用WIN7系统IIS的注意 ...
- spring-boot集成PageHelper和通用Mapper
前提条件:已经集成mybatis 代码生成步骤: 添加依赖 <dependency> <groupId>tk.mybatis</groupId> <artif ...
随机推荐
- 洛谷——P1548 棋盘问题
https://www.luogu.org/problem/show?pid=1548#sub 题目描述 设有一个N*M方格的棋盘(l<=N<=100,1<=M<=100)(3 ...
- Android 解决RecyclerView删除Item导致位置错乱的问题
RecyclerView的刷新分为内容变化和结构变化,结构变化比如remove和insert等并不会导致viewholder的更新,所以有时候我们使用 notifyItemRemoved(positi ...
- python获取序列中最大值
test =[ [1, 2, 3], [4, 5, 6], [7, 8, 9]] #这个就可以看做是二维数组了,直接创建print(test)print(test[:][1]) ...
- netty reactor线程模型分析
netty4线程模型 ServerBootstrap http示例 // Configure the server. EventLoopGroup bossGroup = new EpollEvent ...
- 对Linux下常用头文件总结
asm.current.h 定义全局项current ,其指向结构体struct task_struct linux/sched.h 定义结构体task_struct ,只要包含此头文件 ...
- php 获取数组第一个key 第一个键值对 等等
PHP 获取数组中的第一个元素或最后一个元素的值或者键值可以使用 PHP 自带的数组函数. PHP 获取数组中的第一个元素的值或者键值所使用的函数: current() - 返回数组中当前元素值(即: ...
- UVA 11624 - Fire! 图BFS
看题传送门 昨天晚上UVA上不去今天晚上才上得去,这是在维护么? 然后去看了JAVA,感觉还不错昂~ 晚上上去UVA后经常连接失败作死啊. 第一次做图的题~ 基本是照着抄的T T 不过搞懂了图的BFS ...
- (转)curl 命令使用
原文地址:http://blog.sina.com.cn/s/blog_4b9eab320100slyw.html 可以看作命令行浏览器 1.开启gzip请求curl -I http://www.si ...
- Xvisor ARM32 启动分析
Linux内核历史悠久,特性丰富,但是代码量庞大,各个子系统交叉繁琐.对于想要将操作系统内核各个特性研究一遍的人,有时候也只好"望Linux兴叹".Xvisor是一个较新的Type ...
- 谁要的手机用KRKR2 Onscripter 资源打包工具
本软件能够把你手机上指定文件夹打包为文字冒险游戏资源文件 支持打包 1.Onscripter 的NSA格式 2.吉里吉里2(KRKR2)的XP3.(分2.29曾经的旧版本号和2.30以后新版本号) 3 ...