3. 插件

  • java
  • java
  • mybatis

可供扩展的点有四个:

  • 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...