mybatis-plus自定义拦截器实现数据权限学习(一)

松茂实
2023-12-01

最近由于业务需求开始接触数据权限写了个小demo,项目框架使用的是springboot+mybatis-plus
话不多说

```java
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
@Slf4j
@Component
public class MybatisDataFilterInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
        //获取StatementHandler构造器
        StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(handler, "delegate");
        // 通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
        MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
        SqlCommandType commandType = mappedStatement.getSqlCommandType();
           if (SqlCommandType.SELECT.equals(commandType)) {
               String sqlId = mappedStatement.getId();
               BoundSql boundSql = delegate.getBoundSql();

               String sql = boundSql.getSql();
              
               log.info("sql语句1:{}", sql);
               Statement statement = CCJSqlParserUtil.parse(sql);
               log.info("sql语句2:{}", boundSql.getSql());
               Select select = (Select) statement;
               PlainSelect selectBody = (PlainSelect) select.getSelectBody();
               //根据业务添加过滤条件请自行实现
               StringBuilder sqlFilter = new StringBuilder(128);
               sqlFilter.append("id=5 or 1=1");
               buildWhereClause(selectBody, sqlFilter.toString());
               ReflectUtil.setFieldValue(boundSql, "sql", statement.toString());
           }
            return invocation.proceed();

    }
    
 private void buildWhereClause(PlainSelect select, String dataFilter) throws JSQLParserException {
        if (select.getWhere() == null) {
            select.setWhere(CCJSqlParserUtil.parseCondExpression(dataFilter));
        } else {
            AndExpression and = new AndExpression(
                    CCJSqlParserUtil.parseCondExpression(dataFilter), select.getWhere());
            select.setWhere(and);
        }
    }


和其他网上其他文章不同这个写法是从一个优秀的项目中学习而来
首先通过反射拿到StatementHandler 在这个过程中很多人可能不太熟悉RoutingStatementHandler 观察源码可知:




public class RoutingStatementHandler implements StatementHandler {
    private final StatementHandler delegate;

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        switch(ms.getStatementType()) {
        case STATEMENT:
            this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case PREPARED:
            this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case CALLABLE:
            this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }

    }


RoutingStatementHandler是StatementHandler 的实现类他会根据StatementType 来创建一个代理类,
其实现类作用如下:
SimpleStatementHandler: 管理 Statement 对象并向数据库中推送不需要预编译的SQL语句
PreparedStatementHandler: 管理 Statement 对象并向数据中推送需要预编译的SQL语句,
CallableStatementHandler:管理 Statement 对象并调用数据库中的存储过程

mybatis中默认使用的是PreparedStatementHandler处理器,通过设置statementType来为对应的SQL块指定处理器
而我们需要使用的就是StatementHandler作用主要用于对sql语句的处理
在工作时还会使用 ParameterHandler用于参数处理 和 ResultSetHandler 用于结果映射处理,Executor执行器用于嵌套查询
下一篇我们在说数据权限结合自定义注解实现完整的数据权限
 类似资料: