3. 插件
可供扩展的点有四个:
- Executor:执行器
- StatementHandler:JDBC处理器
- ParameterHandler:参数处理器
- ResultSetHandler:结果集处理器
这四个接口已经涵盖从发起接口调用到SQl声明、参数处理、结果集处理的全部流程。接口中任何一个方法都可以进行拦截改变方法原有属性和行为。不过这是一个非常危险的行为,稍不注意就会破坏MyBatis核心逻辑还不自知。所以在在使用插件之前一定要非常清晰MyBatis内部机制。
实现一个分页插件
@Intercepts(
@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}
)
)
public class MyPageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 带上分页参数
StatementHandler target = (StatementHandler) invocation.getTarget();
// SQL包 sql、参数、参数映射
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();
}
page.setTotal(selectCount(invocation));
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 target = (StatementHandler) invocation.getTarget();
// SQL包 sql、参数、参数映射
String countSql = String.format("select count(*) from (%s) as _page", target.getBoundSql().getSql());
// JDBC
Connection connection = (Connection) invocation.getArgs()[0];
PreparedStatement preparedStatement = connection.prepareStatement(countSql);
target.getParameterHandler().setParameters(preparedStatement);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
count = resultSet.getInt(1);
}
resultSet.close();
preparedStatement.close();
return count;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
Loading...