1、前言
最近使用了mybatis的sql语句动态注入,可以使用@Component的方式进行自动扫描,也可以使用
拦截器技术对已有的sql语句进行拦截。不过拦截器只能拦截已有的sql语句,如果在拦截之前没有sql
语句,将会出现空指针异常,应该是mybatis内部在进行调用的时候出现的空指针,如果说非要在拦截
器里面直接构造sql语句,就必须得找到何处出现了空指针异常,并在其前面对执行语句进行拦截并
构造。
2、入门
Mybatis提供了Interceptor接口,我们只需要实现这个接口里面的相关函数就行了,一个标准的拦截器
应该包含以下几部分:
@Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class}))
@Component
public abstract class AbstructInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable{
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
其中,intercept 方法里面需要实现我们获取原始sql/params,然后对sql进行构造重组并且重新
放入到被拦截待执行的sql中。
先来说说相关的 annolation吧,@Intercepts 注释代表这是一个拦截器,里面可以放入多个待拦截
对象,它们之间以空格隔开。@Signature 注释里面有三个参数,分别是
type:表示拦截的接口,可以拦截的接口一共有四个,比较常见的是Executor.class 和 StatementHandler.class
接口。读了源代码就会发现,实际上 StatementHandler.class 是 Executor.class 的一个子过程。
method:拦截对应接口的相关方法,例如,当拦截Executor.class的时候,就可以去查看其实现的方法,
比较常见的方法有 query和 update 两种。
args:是对应方法实现的参数,比如Executor.class 里面的query 有两种实现方法,分别有两种不同的
形参列表,那么我们在这里进行拦截的时候也可以写两种不同的参数列表,代表了拦截两种不同的query方法。
看了源代码可以发现,实际上,少形参的query在内部调用了多形参的query方法,所以实际上没必要拦截多
形参的方法,否则在少形参里面会被拦截两次,从而产生意料之外的问题
@Component 代表该拦截器是一个bean,在spring boot项目扫描的时候会自动注入到项目中
解决的问题:为了避免mybatis因增加或者减少一两个字段从而需要去修改.xml文件的繁琐性,用拦截器进行动态
生成的通用sql只需要找到那个column,便会自动注入并进行判断,减少了人工维护庞大的.xml mybatis的sql文件
的复杂性。主要用在生成where语句,插入语句的value,更新语句的set等方面
技巧1:实现一个抽象类来实现 Interceptor接口,然后其他子类继承该抽象类即可
// AbstructInterceptor.java
public abstract class AbstructInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable{
invocation(invocation);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
protected abstract void invocation(Invocation invocation);
}
//QueryInterceptor.java
@Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class}))
@Component
public class QueryInterceptor extends AbstructInterceptor {
@Override
protected void invocation(Invocation invocation) {
}
}
技巧2:将实现类xxxInterceptor的实现body抽象出来放在实体文件夹中,在xxxInterceptor直接
进行引入即可,该方法使用了单一职责原则,减少了维护的难度。
一般来说,因为拦截器拦截的对象是mybatis将要执行的全部 query ,所以我们只需要针对baseModel
来写拦截器,其子类可以使用递归的方式进行构造。(或又叫深度优先搜索)
具体怎么写,请观看以下类的详细的sdk
MappedStatement.class
MetaObject.class
Configuration.class
Invocation.class
StatementHandler.class
SqlNode.class
只有详细了解了如何使用这些class对sql进行操作,才能用起来得心应手,否则出现问题就得百度,事情将变得没有意思