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;
} }
2019年4月16日更新,为了使用mybaits缓存机制减少数据库负担,将部分代码改写
@Intercepts({
@Signature(type = ResultSetHandler.class,method = "handleResultSets", args = { Statement.class })
})
public class DictReplaceInteceptor implements Interceptor{
private Properties properties;
private SpringContextHolder spring; @Override
@SuppressWarnings(value = {"all"})
public Object intercept(Invocation invocation) throws Throwable {
//
List<Object> results = (List<Object>)invocation.proceed();
SqlSessionFactory sqlSessionFactory = spring.getBean(SqlSessionFactory.class);
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
if(ToolUtil.isNotEmpty(results)){
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)){ if (ToolUtil.isEmpty(dictId)) {
return "";
} else {
//以前是直接调用方法,每次调用调用都会创建,现在通过sqlSession获取对应的mapper 避免每次都创建
DictMapper dictMapper = sqlSession.getMapper(DictMapper.class);
Dict dict = dictMapper.selectById(new String(dictId.toString()));
if (dict == null) { } else {
field.set(result, dict.getName());
}
}
} }
}
}
}
}catch (Exception e) {
e.printStackTrace();
}finally{
sqlSession.close();
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;
} }
再举一个例子
注解对象
@Target({ElementType.FIELD})//
@Retention(RetentionPolicy.RUNTIME)//该注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Inherited//允许子类继承父类的注解。 (子类中可以获取并使用父类注解)
@Documented//指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。
public @interface One2One {
String byField();
Class resultType();
Class mapper();
String methodName();
}
拦截器
import com.msunsoft.base.common.factory.ConstantFactory;
import com.msunsoft.base.common.interceptor.annotation.One2One;
import com.msunsoft.base.spring.SpringContextHolder;
import com.msunsoft.base.util.ToolUtil;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Statement;
import java.util.List;
import java.util.Properties; @Intercepts({
@Signature(type = ResultSetHandler.class,method = "handleResultSets", args = { Statement.class })
})
public class One2OneInteceptor implements Interceptor { private Properties properties;
private SpringContextHolder spring; @Override
@SuppressWarnings(value = {"all"})
public Object intercept(Invocation invocation) throws Throwable {
//
List<Object> results = (List<Object>)invocation.proceed();
SqlSessionFactory sqlSessionFactory = spring.getBean(SqlSessionFactory.class);
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
if(ToolUtil.isNotEmpty(results)){
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) {
field.setAccessible(true);
One2One one2One = field.getAnnotation(One2One.class);
if(one2One!=null){
String byFieldString = one2One.byField();
Class resultType = one2One.resultType();
Class mapper = one2One.mapper();
String methodName = one2One.methodName();
Object objMaper = sqlSession.getMapper(mapper);
Method method = mapper.getMethod(methodName, Serializable.class); Field byField = cls.getDeclaredField(byFieldString);
byField.setAccessible(true);
field.set(result, method.invoke(objMaper,byField.get(result))); }
}
}
}
}catch (Exception e) {
e.printStackTrace();
}finally{
sqlSession.close();
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;
}
}
使用实例(实体类中)
//项目编号
@TableField("project_id")
private Long projectId; //项目信息
@TableField(exist = false)
@One2One(byField = "projectId",resultType = Project.class,mapper= ProjectMapper.class,methodName = "selectById")
private Project project;
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),
mybaits拦截器+自定义注解的更多相关文章
- SpringVC 拦截器+自定义注解 实现权限拦截
1.springmvc配置文件中配置 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns= ...
- spring拦截器和注解处理日志操作
整体思想:通过拦截器拦截所有的请求,处理含有自定义注解的方法,通过request得到需要的参数. 拦截器代码: package com.zktx.platform.log2; import java. ...
- MyBatis拦截器自定义分页插件实现
MyBaits是一个开源的优秀的持久层框架,SQL语句与代码分离,面向配置的编程,良好支持复杂数据映射,动态SQL;MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyB ...
- 基于SpringMVC拦截器和注解实现controller中访问权限控制
SpringMVC的拦截器HandlerInterceptorAdapter对应提供了三个preHandle,postHandle,afterCompletion方法. preHandle在业务处理器 ...
- SpringMVC之八:基于SpringMVC拦截器和注解实现controller中访问权限控制
SpringMVC的拦截器HandlerInterceptorAdapter对应提供了三个preHandle,postHandle,afterCompletion方法. preHandle在业务处理器 ...
- Struts2知识点小结(四)--拦截器与注解开发
一.Struts2的拦截器(interceptor) 作用:当请求进入struts2框架后(进入之前可以用filter进行拦截),想对请求进行拦截操作(功能增强.权限控制),需要拦截器组件 1.str ...
- SpringMVC(AbstractController,拦截器,注解)
1.Controller接口及其实现类 Controller是控制器/处理器接口,只有一个方法handleRequest,用于进行请求的功能处理(功能处理方法),处理完请求后返回ModelAndVie ...
- struts2拦截器-自定义拦截器,放行某些方法(web.xml配置)
一.web.xml配置 <filter> <filter-name>encodingFilter</filter-name> <filter-class> ...
- Struts2配置拦截器自定义栈时抛异常:Unable to load configuration. - interceptor-ref - file:/D:/tomcat_install/webapps/crm/WEB-INF/classes/struts.xml
代码如下: <interceptors> <!-- 注册自定义拦截器 --> <interceptor name="LoginInterceptor&qu ...
随机推荐
- NSObject
一.前言 该博客里面的方法均是看着苹果官方的API来解释的,一般都是常用的方法如有问题,请指出. 二.简介: 该类集成的是其本身,大家可以从任何一个类去向上追溯,都会发现最终的父类都是NSObject ...
- Alpha冲刺 - 事后诸葛亮
Alpha冲刺 - 事后诸葛亮 Alpha完成情况表 Stardust(安卓端) 模块 预期计划 现实进展 完成度 登录/注册 登录时,从服务器拉取的数据并同步数据库.获取的数据有:用户名.密码.记录 ...
- java基础面试题(Servlet生命周期)
Servlet运行在Servlet容器中,其生命周期由容器来管理.Servlet的生命周期通过javax.servlet.Servlet接口中的init().service()和destroy()方法 ...
- 【转】ICCAVR TAB键设置
转载于: http://blog.163.com/liuyunqian@yeah/blog/static/7039584320099159545292/ 在使用ICCAVR C编译器的时候会发现TAB ...
- WebStorm如何分配运行内存?The IDE is running low on memory...
vue项目做的后台管理系统做得差不多了,安装的依赖包也越来越大,就在春节放假的前两天,突然发现我的电脑居然带不动WebStorm了,查改一些代码,WebStorm运行迟钝,鼠标滑动严重“掉帧”,让我非 ...
- Python 使用 xlwings 往 excel中写入一列数据的两种方法
1.准备一个二维列表,然后再range后面不指定任何选项,可以输出该二维列表中数据在一列中显示,如下代码: # -*- coding:utf-8 -*- import xlwings as xw li ...
- esp8266(1) 手机+Arduino+esp8266通信
ESP8266 Android与Arduino通信 功能描述: 1 Arduino上电,它通过软串口(Arduino的 2号和3号脚)发送命令,配置espson8266为 AP模式,wifi名 DDD ...
- Python高级网络编程系列之第二篇
在上一篇中,我们深入探讨了TCP/IP协议的11种状态,理解这些状态对我们编写服务器的时候有很大的帮助,但一般写服务器都是使用C/Java语言,因为这些语言对高并发的支持特别好.我们写的这些简单的服务 ...
- Linux系统--命令行安装weblogic10.3.6
Linux下命令行安装weblogic10.3.6 一.安装前准备工作: 1.创建用户useradd weblogic;创建用户成功linux系统会自动创建一个和用户名相同的分组,并将该用户分到改组中 ...
- vmware 12中安装苹果系统
我用的系统是win10... 一.所需软件: 1.下载并安装VMware Workstation Pro 12 密码:7ybc和序列号 密码是:bwm0 2.下载unlocker 203(for OS ...