mybaits拦截器+自定义注解
实现目的:为了存储了公共字典表主键的其他表在查询的时候不用关联查询(所以拦截位置位于mybaits语句查询得出结果集后)
项目环境 :springboot+mybaits
实现步骤:自定义注解——自定义实现mybaits拦截器——注册mybaits拦截器
一、自定义注解
1.1 代码示例
import java.lang.annotation.documented; import java.lang.annotation.elementtype; import java.lang.annotation.inherited; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; @target({elementtype.field})// @retention(retentionpolicy.runtime)//该注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在; @inherited//允许子类继承父类的注解。 (子类中可以获取并使用父类注解) @documented//指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。 /** * 该自定义注解用于所查询语句中字段包含字典表主键 并需要将主键同时对照成字典表对应的名称 * 将该注解放置在名称列,参数为字典表主键存储列的名字 * @classname: dictreplace * 描述: todo 用于字典名称字段默认为空,则空则认为字典id字段名为 字典名称字典.substring(0,length()-4) 若不为空则认定字典id字段名称为参数值 * 作者cy * 时间 2019年3月26日 上午9:02:47 * */ public @interface dictreplace { string dictidfieldname() default ""; }
@target 注解
功能:指明了修饰的这个注解的使用范围,即被描述的注解可以用在哪里。
elementtype的取值包含以下几种:
- type:类,接口或者枚举
- field:域,包含枚举常量
- method:方法
- parameter:参数
- constructor:构造方法
- local_variable:局部变量
- annotation_type:注解类型
- package:包
@retention 注解
功能:指明修饰的注解的生存周期,即会保留到哪个阶段。
retentionpolicy的取值包含以下三种:
- source:源码级别保留,编译后即丢弃。
- class:编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。
- runtime: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。
@documented 注解
功能:指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。
@inherited注解
功能:允许子类继承父类中的注解。
1.2 使用场景
@tablefield("runtime_platform") private integer runtimeplatform; @dictreplace//字典替换注解 @tablefield(exist = false) private string runtimeplatformname;
二、自定义mybaits拦截器并注册
2.1 代码示例
import java.util.list; import java.util.properties; import org.apache.ibatis.plugin.interceptor; import org.apache.ibatis.plugin.intercepts; import org.apache.ibatis.plugin.invocation; import org.apache.ibatis.plugin.plugin; import org.apache.ibatis.plugin.signature; import com.msunsoft.base.common.factory.constantfactory; import com.msunsoft.base.common.interceptor.annotation.dictreplace; import com.msunsoft.base.spring.springcontextholder; import com.msunsoft.base.util.toolutil; import org.apache.ibatis.executor.resultset.resultsethandler; import java.lang.reflect.field; import java.sql.statement; /** * 字典替换拦截器,当注解方法被执行后拦截并修改查询后的结果 * @classname: dictreplaceinteceptor * 描述: todo * 作者 * 时间 2019年3月25日 下午7:23:41 * */ @intercepts({ @signature(type = resultsethandler.class,method = "handleresultsets", args = { statement.class }) }) public class dictreplaceinteceptor implements interceptor{ private properties properties; private springcontextholder spring;//实现 applicationcontextaware 接口的类包含获取spring容器中的bean的静态方法 @override @suppresswarnings(value = {"all"}) public object intercept(invocation invocation) throws throwable { //因为 handleresultsets 方法执行结束后可以收到一个list类型的数据结果集,所以虽然该方法的目的是用于结束本次拦截,执行预定方法(handleresultsets)方便下次拦截 list<object> results = (list<object>)invocation.proceed(); try{
//自定义方法用于判断对象是否为空 if(toolutil.isnotempty(results)){
//constantfactory 是自定义的包含常用方法的一个类,现在用到的是它包含在其中的通过字典主键获取字典名称的方法 constantfactory constantfactory = spring.getbean(constantfactory.class); class<?> cls = results.get(0).getclass(); field[] fields = cls.getdeclaredfields();// 获取private修饰的成员变量 获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。 for(object result:results){ for (field field : fields) { //获取我们自定义的注解 dictreplace dictreplace = field.getannotation(dictreplace.class); if(dictreplace!=null){//如果存在这个注解 我们在执行后续方法 string dictidfieldname = dictreplace.dictidfieldname();//获取注解属性值 field idfield = null; if(toolutil.isnotempty(dictidfieldname)){ idfield = cls.getdeclaredfield(dictidfieldname);//获取实体类对应字段 }else{ string fieldname = field.getname();//获取实体类字段名称 string idfieldname = fieldname.substring(0,fieldname.length()-4); idfield = cls.getdeclaredfield(idfieldname); } idfield.setaccessible(true);//允许我们在用反射时访问私有变量 object dictid = idfield.get(result);//从返回值中获得字段对应的 值 field.setaccessible(true); if(toolutil.isnotempty(dictid)){ field.set(result, constantfactory.getdictname( long.valueof(new string(dictid.tostring())) ) ); //用字典id查询出字典名称 并替换结果集中的值 } } } } } }catch (exception e) { e.printstacktrace(); }finally{ return results; } } @override public object plugin(object target) { // 读取@signature中的配置,判断是否需要生成代理类 if (target instanceof resultsethandler) { return plugin.wrap(target, this);//返回代理 } else { return target; } } @override public void setproperties(properties properties) { this.properties = properties; } }
2.2 拦截器部分知识点
2.1.1 mybatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,mybatis 允许使用插件来拦截的方法调用包括:
- executor (update, query, flushstatements, commit, rollback, gettransaction, close, isclosed)
- parameterhandler (getparameterobject, setparameters)
- resultsethandler (handleresultsets, handleoutputparameters)
- statementhandler (prepare, parameterize, batch, update, query)
2.1.2 mybatis拦截器的接口定义
一共有三个方法intercept
、plugin
、setproperties
setproperties()
方法主要是用来从配置中获取属性。
plugin()
方法用于指定哪些方法可以被此拦截器拦截。
intercept()
方法是用来对拦截的sql
进行具体的操作。
注解实现
mybatis
拦截器用到了两个注解:@intercepts
和@signature
@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}), } )
type
的值与类名相同,method
与方法名相同,为了避免方法重载,args
中指定了各个参数的类型和个数,可通过invocation.getargs()
获取参数数组。
2.1.3 spring boot整合
方法一
如果是使用xml式配置拦截器,可在mybatis配置文件中添加如下节点,属性可以以如下方式传递
<plugins> <plugin interceptor="tk.mybatis.simple.plugin.xxxinterceptor"> <property name="propl" value="valuel" /> <property name="prop2" value="value2" /> </plugin> </plugins>
方法二
如果在spring boot
中使用,则需要单独写一个配置类,如下:
@configuration public class mybatisinterceptorconfig { @bean public string myinterceptor(sqlsessionfactory sqlsessionfactory) { executorinterceptor executorinterceptor = new executorinterceptor(); properties properties = new properties(); properties.setproperty("prop1","value1"); executorinterceptor.setproperties(properties); return "interceptor"; } }
or
import com.baomidou.mybatisplus.extension.plugins.optimisticlockerinterceptor; import com.baomidou.mybatisplus.extension.plugins.paginationinterceptor; import com.msunsoft.base.common.interceptor.mybaits.dictreplaceinteceptor; import org.mybatis.spring.annotation.mapperscan; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; import org.springframework.transaction.annotation.enabletransactionmanagement; @configuration @enabletransactionmanagement @mapperscan("com.msunsoft.**.mapper")//mapper接口扫描 public class datasourceconfig { /** * 乐观锁mybatis插件 */ @bean public optimisticlockerinterceptor optimisticlockerinterceptor() { return new optimisticlockerinterceptor(); } /** * mybatis-plus分页插件 */ @bean public paginationinterceptor paginationinterceptor() { return new paginationinterceptor(); } @bean public dictreplaceinteceptor dictreplaceinteceptor(){ return new dictreplaceinteceptor(); } }
方法三
在拦截器上加@component注解
ps:
一、引用并参考
1.《深入理解mybatis原理》 mybatis的架构设计以及实例分析
https://blog.csdn.net/luanlouis/article/details/40422941
2.关于mybatis拦截器,对结果集进行拦截
https://www.cnblogs.com/smallhan/articles/8127327.html
3.springboot2(22)mybatis拦截器实现
https://blog.csdn.net/cowbin2012/article/details/85256360
二、涉及技术点
spring(注解、aop) ,java反射与动态代理,mybaits(以上代码示例用的是mybaits-plus 3.0.6.jar),