1、先看.yml配置文件中的写法配置:(boot-2.0.1)
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.dalong
configuration:
lazy-loading-enabled: true
aggressive-lazy-loading: false
interceptors: {"com.dalong.interceptor.MyInterceptor"}
,里面还有一些别的配置,主要看interceptors就好了。
2、Mybatis拦截器类:
/**
* FileName: MyInterceptor
* Author: dalong
* Date: 2018/12/19/019 15:03
* Description:
* History:
* <author> <date> <desc>
* 作者姓名 修改时间 描述
*/
package com.dalong.interceptor;
import com.dalong.page.PageParams;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@Component //这个注解很重要。
//设置分页插件的签名
@Intercepts({
@Signature(type = StatementHandler.class,
method = "prepare",
args={Connection.class,Integer.class})
})
public class MyInterceptor implements Interceptor{
private final Logger logger= LoggerFactory.getLogger(MyInterceptor.class);
private Integer defaultPage;//默认页码
private Integer defaultPageSize;//默认每页条数
private Boolean defaultUseFlag;//默认是否启用插件
private Boolean defaultCheckFlag;//默认是否检测当前页码的正确性
/**
* 设置参数
*/
@Override
public void setProperties(Properties properties) {
String strDefaultPage = properties.getProperty("default.page", "1");
String strDefaultPageSize = properties.getProperty("default.pageSize", "50");
String strDefaultUseFlag = properties.getProperty("default.useFlag", "false");
String strDefaultCheckFlag = properties.getProperty("default.checkFlag", "false");
this.defaultPage = Integer.parseInt(strDefaultPage);
this.defaultPageSize = Integer.parseInt(strDefaultPageSize);
this.defaultUseFlag = Boolean.parseBoolean(strDefaultUseFlag);
this.defaultCheckFlag = Boolean.parseBoolean(strDefaultCheckFlag);
}
/**
* 生成代理对象
*/
@Override
public Object plugin(Object o) {
return Plugin.wrap(o,this);
}
/**
* 插件运行的代码,它将代替原有的方法,要重写最重要的intercept方法
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = getUnProxyObject(invocation);
MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
System.out.println("myinterceptor sql:----"+sql);
logger.error(sql);
//不是select语句
if(!checkSelect(sql)){
return invocation.proceed();
}
BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
Object parameterObject = boundSql.getParameterObject();
PageParams pageParams = getPageParams(parameterObject);
if(pageParams == null){ //没有分页参数,不启用插件
return invocation.proceed();
}
//获取分页参数,获取不到使用默认值。
Integer pageNum = pageParams.getPage() == null ? this.defaultPage : pageParams.getPage();
Integer pageSize = pageParams.getPageSize() == null ? this.defaultPageSize : pageParams.getPageSize();
Boolean useFlag = pageParams.getUseFlag() == null ? this.defaultUseFlag : pageParams.getUseFlag();
Boolean checkFlag = pageParams.getCheckFlag() == null ? this.defaultCheckFlag : pageParams.getCheckFlag();
if(!useFlag){
return invocation.proceed();
}
int total = getTotal(invocation,metaStatementHandler,boundSql);
//回填总数到分页参数里
setTotalToPageParams(pageParams,total,pageSize);
//检查当前页码的有效性
checkPage(checkFlag,pageNum,pageParams.getTotalPage());
return changeSql(invocation, metaStatementHandler, boundSql,pageNum,pageSize);
}
/**
* 功能描述: 从代理对象分离出真实对象
*
*@param invocation
* @return: 非代理StatementHandler 对象
* @since: 1.0.0
* @Author:dalong
* @Date:
*/
private StatementHandler getUnProxyObject(Invocation invocation){
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
//分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过循环可以分离出最原始的目标类)
Object object = null;
while (metaStatementHandler.hasGetter("h")){
object = metaStatementHandler.getValue("h");
}
if(object == null){
return statementHandler;
}
return (StatementHandler) object;
}
/**
* 功能描述: 判断是否是select语句。
*
*@param
* @return:
* @since: 1.0.0
* @Author:dalong
* @Date:
*/
private boolean checkSelect(String sql){
String trimSql = sql.trim();
int idx = trimSql.toLowerCase().indexOf("select");
return idx == 0;
}
/**
* 功能描述: 获取分页参数,这里支持使用Map和@Param注解传递参数,或者POJO继承PageParams,三种方式。
*
*
* @return:
* @since: 1.0.0
* @Author:dalong
* @Date:
*/
private PageParams getPageParams(Object parameterObject){
if(parameterObject == null){
return null;
}
PageParams pageParams = null;
//支持MAP参数和MyBatis的@Param注解参数
if(parameterObject instanceof Map){
@SuppressWarnings("unchecked")
Map<String,Object> paramMap = ( Map<String,Object>) parameterObject;
Set<String> keySet = paramMap.keySet();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()){
String key = iterator.next();
Object value = paramMap.get(key);
if(value instanceof PageParams){
return (PageParams) value;
}
}
}else if(parameterObject instanceof PageParams){
pageParams = (PageParams) parameterObject;
}
return pageParams;
}
/**
* 功能描述: 获取总数
* @return:
* @since: 1.0.0
* @Author:dalong
* @Date:
*/
private int getTotal(Invocation invocation,MetaObject metaStatementHandler,BoundSql boundSql) throws Throwable {
//获取当前的MappedStatement
MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
//配置对象
Configuration cfg = mappedStatement.getConfiguration();
//当前需要执行的sql
String sql = (String)metaStatementHandler.getValue("delegate.boundSql.sql");
//改写为统计总数的sql,此处用mysql库,其他的需要改写。
String countSql = "select count(*) as total from ("+sql+") $_paging";
//获取拦截的方法参数,是Connection对象。
Connection conn = (Connection)invocation.getArgs()[0];
PreparedStatement ps = null;
int total = 0;
try {
//预编译统计总数sql
ps = conn.prepareStatement(countSql);
//构建统计总数boundsql
BoundSql countBoundSql = new BoundSql(cfg,countSql,boundSql.getParameterMappings(),boundSql.getParameterObject());
//构建MyBatis的ParameterHandler用来设置总数sql的参数
ParameterHandler handler = new DefaultParameterHandler(mappedStatement,boundSql.getParameterObject(),countBoundSql);
//设置总数sql参数
handler.setParameters(ps);
//执行查询
ResultSet rs = ps.executeQuery();
while(rs.next()){
total = rs.getInt("total");
}
}finally {
if(ps != null){
ps.close();
}
}
System.err.println("总条数:"+ total);
return total;
}
/**
* 功能描述: 回填总条数和总页数
* @return:
* @since: 1.0.0
* @Author:dalong
* @Date:
*/
private void setTotalToPageParams(PageParams pageParams,int total,int pageSize){
pageParams.setTotal(total);
//计算总页数
int totalPage = total % pageSize == 0 ? total/pageSize : total/pageSize + 1;
pageParams.setTotalPage(totalPage);
}
/**
* 功能描述: 判断当前页码是否大于最大页码
* @return:
* @since: 1.0.0
* @Author:dalong
* @Date:
*/
private void checkPage(Boolean checkFlag,int pageNum,int totalPage) throws Throwable {
if(checkFlag){
//检查页码page是否合法
if(pageNum > totalPage){
throw new Exception("查询失败,查询页码【"+pageNum+"】 大于总页数 【"+totalPage+"】!!");
}
}
}
/**
* 功能描述: 改写sql以满足分页的需求。
* @return:
* @since: 1.0.0
* @Author:dalong
* @Date:
*/
private Object changeSql(Invocation invocation, MetaObject metaStatementHandler,BoundSql boundSql,
int pageNum,int pageSize) throws Exception {
//获取当前要执行的sql
String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
//修改sql,使用的mysql,其他的需要按修改
String newSql = "select * from ("+sql+") $_paging_table limit ?,?";
//修改要执行的sql
metaStatementHandler.setValue("delegate.boundSql.sql",newSql);
//相当于调用StatementHandler的prepare方法,预编译了当前的sql并设置原有的参数,但是少了两个分页参数,它返回的是一个preparedStatement对象
PreparedStatement ps = (PreparedStatement) invocation.proceed();
//计算sql总参数个数
int count = ps.getParameterMetaData().getParameterCount();
//设置两个分页参数
ps.setInt(count-1,(pageNum-1)*pageSize);
ps.setInt(count,pageSize);
return ps;
}
}
以上是自己照着书手抄的,只是结合了springboot的使用。
调的过程中,发现一直找不到prepare方法,就网上看了一下,MyBatis 3.4.0 之后,StatementHandler的prepare方法做了修改....具体可以看地址:https://blog.csdn.net/zsq520520/article/details/69666734
还有一个小问题,就是 setProperties 方法好像没有起作用,所以传入pageParams的时候所有初始需要的参数赋值哈,再进一步调调看。囧。。。。囧。。。。。。。囧。。。