@Intercepts(@Signature(type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}))
@SuppressWarnings("all")
public class PageInterceptor implements Interceptor {
/**
* 执行到这里
*
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
//1 检测是否满足分页条件
//1.1 带上分页参数
StatementHandler target = (StatementHandler) invocation.getTarget();
//1.2sql包
BoundSql boundSql = target.getBoundSql();
Object parameterObject = boundSql.getParameterObject();
Page page = null;
if (parameterObject instanceof Page) {
page = (Page) parameterObject;
} else if (parameterObject instanceof Map) {
page = (Page) ((Map) parameterObject).values().stream().filter(v -> v instanceof Page).findFirst().orElse(null);
}
if (page == null){
return invocation.proceed();
}
if (page != null) {
//设置总行数 select count(1) from user;
int count = selectCount(invocation);
page.setTotal(count);
}
//修改原有的sql select * from user offset 0,limit 50;
String newSql = String.format("%s limit %s offset %s",boundSql.getSql(),page.getSize(),page.getOffset());
SystemMetaObject.forObject(boundSql).setValue("sql",newSql);
//直接放行
return invocation.proceed();
}
private int selectCount(Invocation invocation) throws SQLException {
int count = 0;
StatementHandler taget = (StatementHandler) invocation.getTarget();
String countSql = String.format("select count(*) from (%s) as _page", taget.getBoundSql());
Connection conneciton = (Connection) invocation.getArgs()[0];
PreparedStatement preparedStatement = conneciton.prepareStatement(countSql);
//设置参数
taget.getParameterHandler().setParameters(preparedStatement);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()){
count = resultSet.getInt(0);
}
resultSet.close();
preparedStatement.close();
return count;
}
@Override
public Object plugin(Object target) {
return null;
}
@Override
public void setProperties(Properties properties) {
}
}
mybatis分页插件
SqlSession执行增删改查都是委托给Executor完成的
Executor主要完成以下几项内容:
- 处理缓存,包括一级缓存和二级缓存
- 获取数据库连接
- 创建Statement或者PrepareStatement对象
- 访问数据库执行SQL语句
- 处理数据库返回结果
CachingExecutor
CachingExecutor用于处理二级缓存,如果缓存中不存在要查询的数据,那么将查询请求委托给其他的Executor。如果是执行SQL的增删改,那么CachingExecutor将清空二级缓存
BaseExecutor
当调用该类的查询方法时,先查看一级缓存中是否已经有数据,如果有则直接从缓存获取,如果没有调用子类的查询方法从数据库中获取。
当调用该类的update方法(mybatis将delete和insert认为是update,统一调用该类update方法)时,BaseExecutor将一级缓存清空,然后调用子类对应的增删改方法。
调用执行rollback/commit方法时,该类清空一级缓存
SimpleExecutor
SimpleExecutor继承自BaseExecutor,该类比较简单。
当执行增删改查时,该类获取数据库连接,创建PrepareStatement或者Statement对象,执行SQL语句,最后将数据库返回结果转化为设定的对象
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 获取StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 调用prepareStatement()方法,创建Statement对象,并进行设置参数等操作
stmt = prepareStatement(handler, ms.getStatementLog());
// 调用StatementHandler对象的query()方法执行查询操作
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
SimpleExecutor基本是按照标注JDBC流程执行SQL语句获得返回结果
BatchExecutor
当执行查询时,BatchExecutor与SimpleExecutor的处理逻辑是一样的。不同的是执行更新方法(mybatis认为delete和insert都是update)。执行更新方法时,BatchExecutor不是直接执行SQL语句,而是将其放到批次里面,等到提交的时候一起执行
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
final Configuration configuration = ms.getConfiguration();
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
final BoundSql boundSql = handler.getBoundSql();
final String sql = boundSql.getSql();
final Statement stmt;
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
int last = statementList.size() - 1;
stmt = statementList.get(last);
applyTransactionTimeout(stmt);
handler.parameterize(stmt);//fix Issues 322
BatchResult batchResult = batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt); //fix Issues 322
currentSql = sql;
currentStatement = ms;
statementList.add(stmt);
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
// handler.parameterize(stmt);
handler.batch(stmt);
return BATCH_UPDATE_RETURN_VALUE;
}
ReuseExecutor
ReuseExecutor从名字可以看出该类主要特点是复用。它复用的是Statement对象或者PreparedStatement对象。
该类中有一个属性statementMap,如下面代码所示,key是SQL语句,value是Statement对象。每次执行首先根据SQL语句查询statementMap,如果有对应的Statement对象,则直接使用该Statement对象,如果没有,则创建新的Statement对象,然后将其和SQL语句添加到statementMap