Mybatis与PageHelper⼀起使⽤⽆法拦截分页前的sql解决⽅案
在使⽤mybatis的时候,遇到⼀个很蛋疼的问题,就是⽆法拦截pagehelper分页插件执⾏之前的sql,每次拦截都是已经拼接完sql,⽽且⽆法拦截pagehelper中那个select count语句,然⽽今天我来给⼤家讲下我是如何解决这个问题的
我在⽹上查过N篇⽂章,结果都是直接给出了pagehelper的官⽅⽂档,⼀开始看到别⼈写的时候还以为这个⼈这么6的,结果这是官⽅⽂档~,这⾥我也不多说了,先将官⽹放出来你们有空可以看看
通过官⽅描述我们不难发现,其实我们⽆法获取分页之前的sql是因为他⽐我们早⼀步拦截,所以不管我们怎么获取都是被处理完的sql
所以我们去查看他的源码,通过源码查看不难发现,其实他们主要使⽤PageInterceptor进⾏拦截
@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, Cac heKey.class, BoundSql.class}),
}
)
public class PageInterceptor implements Interceptor {
由上述代码我们可以看到,他是拦截了Executor这个类的query⽅法,并且拦截了4个参数或者6个参数,具体参看上⾯官⽅⽂档
由于mybatis的顺序是先拦截4个参数的⽅法,然后在到6个参数的⽅法,所以官⽅⽂档有这么⼀个描述
所以我们就得直接拦截四个参数的⽅法,然后再跳到PageInterceptor,这样我们就能先拿到sql了,所以我们来尝试⼀下
@Intercepts(
{
分页查询插件
@Signature(type = Executor.class, method ="query", args ={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
}
)
@Component
public class PowerInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation)throws Throwable {
Object[] args = Args();
MappedStatement ms =(MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds =(RowBounds) args[2];
ResultHandler resultHandler =(ResultHandler) args[3];
Executor executor =(Executor) Target();
CacheKey cacheKey;
BoundSql boundSql;
//由于逻辑关系,只会进⼊⼀次
if(args.length ==4){
//4 个参数时
boundSql = ms.getBoundSql(parameter);
cacheKey = ateCacheKey(ms, parameter, rowBounds, boundSql);
}else{
//6 个参数时
cacheKey =(CacheKey) args[4];
boundSql =(BoundSql) args[5];
}
//TODO ⾃⼰要进⾏的各种处理
//注:下⾯的⽅法可以根据⾃⼰的逻辑调⽤多次,在分页插件中,count 和 page 各调⽤了⼀次
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
@Override
public Object plugin(Object target){
return Plugin.wrap(target,this);
}
@Override
public void setProperties(Properties properties){
}
}
⾸先我建了⼀个类叫PowerInterceptor,⾥⾯的实现⽅法和⽂档中的QueryInterceptor中的代码⼀样,但是我们拦截了4个参数的⽅法,然后测试发现,还是不得,他是先执⾏QueryInterceptor>PageInterceptor,然后这个PowerInterceptor的拦截直接跳过了?因为QueryInterceptor直接执⾏了下⾯这句话,导致他不会再次进⼊四个参数的拦截,然后只能拦截六个参数的⽅法?
executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
然后我再看了⼀下⽂档,喔,原来是要配置xml的,好,我配!
毕竟我们spring boot⽤惯了就很少⽤过xml来配置了,所以今天来写下吧
在application.properties或者l⽂件中加⼊,下⾯代码为properties⽂件
然后在resource⽬录新建l,添加以下内容
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-////DTD Config 3.0//EN"
"/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 分页助⼿ -->
<plugins>
<plugin interceptor="net.ourway.pagehelper.QueryInterceptor"/>
<plugin interceptor="net.ourway.interceptor.PowerInterceptor"/>
</plugins>
</configuration>
看到这,是不是觉得只要我们PowerInterceptor优先于QueryInterceptor就⾏了,⾄于为什么配置是在QueryInterceptor下⼀⾏,还是那句话,先看⽂档。。。
其实我⼀开始也是以为这就⾏了,结果mmp,什么都没改变,还是先执⾏QueryInterceptor>PageInterceptor,PageInterceptor不执⾏,还是只有拦截六个参数的⽅法的时候才能拦截,顺序还是在PageInterceptor后
emmm,这⿁东西太烦⼈了,然后经过⼀番思考,那我能不能将PageInterceptor也配上,让他在PowerInterceptor之后执⾏,好吧既然想了就来试试
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-////DTD Config 3.0//EN"
"/dtd/mybatis-3-config.dtd">
<configuration>
<!--分页助⼿-->
<plugins>
<plugin interceptor="net.ourway.pagehelper.PageInterceptor"/>
<plugin interceptor="net.ourway.pagehelper.QueryInterceptor"/>
<plugin interceptor="net.ourway.interceptor.PowerInterceptor"/>
</plugins>
</configuration>
感觉思路很正常,没错这是个要⼤成的征兆,是吧
结果,就是我们程序员的⽇常,“感觉”⾃⼰的思路完全可⾏,然后出现以下代码
Caused by: java.lang.RuntimeException:在系统中发现了多个分页插件,请检查系统配置!
我淦!
意思是PageInterceptor被配置了,如果我们再配置就变多个了
此处1w个奔腾⽽过,这该如何是好
最终我做了⼀个决定,将pagehelper抽出来放在⾃⼰的程序上配置,⽽不是在依赖中,我们先把page
helper的依赖去掉,然后点击下⾯链接在github上拿到pagehelper源码,然后将pagehelper全部源码迁进⾃⼰项⽬的包下
然后添加部分依赖
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>3.2</version>
</dependency>
<dependency>
<groupId&le.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
我曹,好像⾏了
然后修改⼀下PowerInterceptor的拦截。在args.length == 4也就是拦截四个⽅法的时候修改sql,ReflectUtil在另⼀章节已给出,
现在测试⼀条数据拦截
@Override
public Object intercept(Invocation invocation)throws Throwable {
Object[] args = Args();
MappedStatement ms =(MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds =(RowBounds) args[2];
ResultHandler resultHandler =(ResultHandler) args[3];
Executor executor =(Executor) Target();
CacheKey cacheKey;
BoundSql boundSql;
//由于逻辑关系,只会进⼊⼀次
if(args.length ==4){
//4 个参数时
boundSql = ms.getBoundSql(parameter);
//测试拦截
String Sql();
sql="select * from ("+sql +") as p where p.Id = '22c66bebfa2445aa99965df7e3e5b318'";
ReflectUtil.setFieldValue(boundSql,"sql", sql);
cacheKey = ateCacheKey(ms, parameter, rowBounds, boundSql);
}else{
//6 个参数时
cacheKey =(CacheKey) args[4];
boundSql =(BoundSql) args[5];
}
//TODO ⾃⼰要进⾏的各种处理
//注:下⾯的⽅法可以根据⾃⼰的逻辑调⽤多次,在分页插件中,count 和 page 各调⽤了⼀次
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
然后测试结果
到这说明已经拦截成功了,在分页之前修改了sql,然后通过分页插件执⾏了select count和limit
但是这个⽅法有点笨,毕竟要将pagehelper源码拉过来,虽然不是很多,其实如果能解决那个PageInterceptor加载就不⽤将代码拉过来了,如果有⼈可以⽤其他办法解决请私聊我,我也想知道
ReflectUtil⼯具类和数据权限拦截的具体实现请点击下⾯链接

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。