最近由于业务需求开始接触数据权限写了个小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执行器用于嵌套查询
下一篇我们在说数据权限结合自定义注解实现完整的数据权限