项目终于搞完一个版本了,最近几天可以一边慢慢修改bug,一边愉快的摸鱼了。哈哈哈。
结果项目经理屁颠屁颠的跑过来,“王哥呀,客户那边新加了个需求,一点小小的改动,几分钟就完成了。”
我:“我很忙的,还有很多bug需要改,等我这几天忙完了再说吧”。
产品经理:“客户说了这个需求必须实现,不然不让上线,就加个简单的权限,让管理员只看到配置了的区域权限就好了”。
我:“!!!!!!!这就是你说的小需求,差点可以让数据库重新设计一遍了”。
没办法呀,需求必须得做呀。只能想下如何快速做完,然后愉快的去摸鱼。。。。
首先我去数据库检查了下,涉及的各种记录查询表大约20来张表,每张表加个区域权限字段是必须的了。。。
然后需要在处理查询的时候,根据登录用户添加上权限的过滤。难道我一个一个查询去加?那我不是不能愉快的摸鱼了。。
既然都是在查询的sql加上一样的权限代码,那我想何不利用mybatis的拦截器拦截sql统一给处理了。
立马百度一下,一顿操作,搞了个拦截器,简单的写了个测试,查询语句也给拼接上了。正想着轻松呀,终于没什么事,可以刷个抖音啥的了。哈哈哈。。。
最后打开前端页面检查了下,全部报错了?????什么鬼,不是没问题吗?赶快看下日志,一看,在分页的情况下,sql拼接都是在分页后拼接的,导致拼接的后的sql报错了。
这尼玛什么情况,我想要的是在分页之前对sql进行处理呀。。。赶快去看了下PageHelper的核心拦截器PageInterceptor,顺便去官网 瞅了一眼,发现了一个有用的东西。
看了半天这个QueryInterceptor规范,虽然我也不知道我看懂没有,哈哈哈。
这里面说的什么意思呢,就是说Executor的实现类CachingExecutor和BaseExecutor的以下方法都一样的,那我们可以直接在拦截器里面把这段代码拷贝进去,对于后面的拦截器就暴露6个参数的方法就行了。
这个是pageHelper给出的示例(注意加了--------的注释)
@Intercepts(
{
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
public class QueryInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
//由于逻辑关系,只会进入一次
if(args.length == 4){
//4 个参数时
//---------------这里就是拷贝的CachingExecutor或者BaseExecutor的代码
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
//6 个参数时
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
//TODO 自己要进行的各种处理
//注:下面的方法可以根据自己的逻辑调用多次,在分页插件中,count 和 page 各调用了一次
//-------------本来一般情况下是执行invocation.proceed(),继续执行拦截方法,但这里直接执行这个方法,相
//-------------当于替换了CachingExecutor或者BaseExecutor原来的实现。
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
我看了下PageInterceptor的实现,也是按照这种原理实现的。所以我们也可以模仿着这种方法写个拦截器,这样就没问题。所以在上面代码TODO那里做了以下的代码逻辑开发,REGION_CODE 就是数据库的权限字段,然后拼接了sql。
String sql = REGION_CODE + " in ('" +join +"')";
String originalSql = boundSql.getSql();
originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + sql;
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), originalSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, newBoundSql);
赶快测试一看,发现还是有问题,PageInterceptor始终在我自己开发的拦截器之前执行。。。。查了下,是由于拦截器加载顺序的原因造成的。加载顺序1>2>3执行顺序就会变成3>2>1。
为了让我们的拦截器先执行,我们需要在PageInterceptor拦截器后面添加我们的拦截器。关键项目是springboot项目,默认会先加载自己项目的拦截器。
尝试了很多方法,包括@Order注解(添加拦截器没实现利用order排序)
@AutoConfigureBefore(这个是控制自动注入的配置类的加载顺序的)
---------------------------------------------------------------------------------------------------------
既然没有方法了,那我只有搞个骚的来解决了,哈哈哈。在我添加自己的拦截器前注入PageHelper的配置类,那么肯定先于我的拦截器添加。
@Configuration
public class AddMybatisInterceptorConfiguration {
@Resource
private List<SqlSessionFactory> sqlSessionFactoryList;
@Resource
private PageHelperAutoConfiguration pageHelperAutoConfiguration;
@PostConstruct
public void addPageInterceptor() {
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
//这个调用没有什么实际意义,只是为了检查PageHelperAutoConfiguration确实注入了
pageHelperAutoConfiguration.pageHelperProperties();
//这个QueryInterceptor就是我实现的添加拦截代码的实现类
sqlSessionFactory.getConfiguration().addInterceptor(new QueryInterceptor());
}
}
}
这样一搞,然后测试,果然没有问题了。
注意点:
1.拦截器的type是Executor的,别用成了StatementHandler、PameterHandler和ResultSetHandler。(用成其他的无论如何配置都会在分页后执行)
2.记得添加拦截器之前注入PageHelper的配置类。