当同时引入通用Mapper
与PageHelper
两款插件的时候,会存在报错的可能。
如果像这样,先执行通用Mapper,再执行分页插件就会出错
<plugin interceptor="com.github.abel533.mapperhelper.MapperInterceptor">
<property name="mappers" value="com.github.abel533.mapper.Mapper"/>
plugin>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
plugin>
Mybatis中存在一个拦截器的回调接口,可以在拦截Myabatis四大杀器Executor、StatementHandler
,ParameterHandler
,ResultHandler
。为什么只能拦截这四个接口?,因为它们都调用了拦截器栈。
在Configuration
中实例化了这几个组件:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object 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 (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler)this.interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
ResultSetHandler resultSetHandler = (ResultSetHandler)this.interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
上面的四个方法都调用了pluginAll
,interceptorChain是一种责任链的设计模式
当自定的interceptor
加入到拦截器链中,就可以获取到上面四个对象,通过拿到对象的属性进一步扩展,例如修改SQL,大部分的情况下都是针对SQL进行自定义的扩展,PageHelper
与通用Mapper
就是这样处理的。
下面我们来谈谈为什么出错?
这个问题出在通用Mapper这里
MapperInterceptor
在做拦截的时候会将MappedStatement
的执行对象替换为MapperProvider
对象,MapperProvider
是通用Mapper
自己定义的,在原有的基础上进行封装增强,为我们封装了一些常用的CRUD
操作,所以我们一般只需要继承通用Mapper
的接口就可以实现大部分逻辑。但是它的实例化必须依赖带参数的构造函数
public class MapperProvider extends MapperTemplate {
public MapperProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
而分页插件需要调用的原生的MappedStatement
对象来完成一些操作,在创建SqlSource
对象时,利用反射来获取SQL
private SqlSource createSqlSource(Object parameterObject) {
try {
String sql;
if (this.providerTakesParameterObject) {
sql = (String)this.providerMethod.invoke(this.providerType.newInstance(), parameterObject);
} else {
sql = (String)this.providerMethod.invoke(this.providerType.newInstance());
}
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
StaticSqlSource sqlSource = (StaticSqlSource)this.sqlSourceParser.parse(sql, parameterType, new HashMap());
return new OrderByStaticSqlSource(sqlSource);
} catch (Exception var5) {
throw new BuilderException("Error invoking SqlProvider method (" + this.providerType.getName() + "." + this.providerMethod.getName() + "). Cause: " + var5, var5);
}
}
重点在sql = (String)this.providerMethod.invoke(this.providerType.newInstance(),
这个调用无参构造器来实例化的,现在通过拦截器链调用到这里就providerType
就变为MapperProvider
,前面提到过的MapperProvider
没有默认的构造函数支持,所以创建失败。
调整顺序先用分页插件,再用通用mapper
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
plugin>
<plugin interceptor="com.github.abel533.mapperhelper.MapperInterceptor">
<property name="mappers" value="com.github.abel533.mapper.Mapper"/>
plugin>