第一步:在mybatis配置文件中配置拦截器
<plugins>
<plugin interceptor="x.x.x.x.x.x.xxxInterceptor" />
</plugins>
第二步:实现拦截器
package com.mg.background.common.persistence.interceptor;
import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import com.mg.background.common.persistence.Pages;
import com.mg.background.common.persistence.utils.Reflections;
//拦截器注解,拦截器必须实现Interceptor接口
//AbstractInterceptor实现了Interceptor接口
//@Intercepts的参数中为@Signature的数组,表示要拦截的类和类中要拦截的方法
//args为这个方法需要的参数
//MappedStatement.class 为一个节点对象 即<select/><insert/>之类
//RowBounds.class分页对象 他的offset和limit属性表示要展示数据的范围
//ResultHandler.class结果集映射
@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) })
public class PaginationInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 4989671349466153547L;
@Override
public Object intercept(Invocation invocation) throws Throwable {
final MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
//boundSql为一条sql语句动态运行对象
//mappedStatement.getBoundSql(parameter);动态计算sql运行的条件
//即<if/>条件的计算,然后生成要运行的sql
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
//得到入参类型
Object parameterObject = boundSql.getParameterObject();
//获取分页参数对象
Pages<Object> pages = null;
if (parameterObject != null) {
//利用反射机制访问对象属性
pages = convertParameter(parameterObject, pages);
}
//如果设置了分页对象,则进行分页
if (pages != null && pages.getPageSize() != -1) {
if (StringUtils.isBlank(boundSql.getSql())) {
return null;
}
String originalSql = boundSql.getSql().trim();
//得到总记录数
pages.setTotal(SQLHelper.getCount(originalSql, null, mappedStatement, parameterObject, boundSql, log));
//分页查询 本地化对象 修改数据库注意修改实现
String pageSql = SQLHelper.generatePageSql(originalSql, pages, DIALECT);
// if (log.isDebugEnabled()) {
// log.debug("PAGE SQL:" + StringUtils.replace(pageSql, "\n", ""));
// }
invocation.getArgs()[2] = new RowBounds(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT);
BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), pageSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
//解决MyBatis 分页foreach 参数失效 start
if (Reflections.getFieldValue(boundSql, "metaParameters") != null) {
MetaObject mo = (MetaObject) Reflections.getFieldValue(boundSql, "metaParameters");
Reflections.setFieldValue(newBoundSql, "metaParameters", mo);
}
//解决MyBatis 分页foreach 参数失效 end
MappedStatement newMs = copyFromMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql));
invocation.getArgs()[0] = newMs;
}
// }
return invocation.proceed();
}
//绑定插件
@Override
public Object plugin(Object target) {
//如果target有接口返回代理类,没有返回类本身
//如果代理类wrap方法会绑定一个拦截器,调用plugin的invoke方法
//将拦截的方法的参数初始化成一个Invocation对象 然后调用intercept方法
return Plugin.wrap(target, this);
}
//初始化拦截器时将配置文件中拦截器下配置的properties设置进来
@Override
public void setProperties(Properties properties) {
super.initProperties(properties);
}
private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
if (ms.getKeyProperties() != null) {
for (String keyProperty : ms.getKeyProperties()) {
builder.keyProperty(keyProperty);
}
}
builder.timeout(ms.getTimeout());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.cache(ms.getCache());
return builder.build();
}
public static class BoundSqlSqlSource implements SqlSource {
BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
}