mybatis update set 多个字段_开发MyBatis自定义Interceptor拦截器的使用

开发MyBatis自定义Interceptor拦截器的使用

一、使用背景

笔者前几天分享的在Spring Cloud架构中,关于数据权限的处理。笔者在团队中采用的就是通过MyBatis拦截器,拦截sql语句,动态组装sql来完成添加数据权限的配置,本文笔者分享MyBatis自定义Interceptor的使用。

二、拦截器注解

mybatis实现自定义拦截器实现过程:

(1)实现org.apache.ibatis.plugin.Interceptor接口;

(2)添加拦截器注解:org.apache.ibatis.plugin.Intercepts;

(3) 配置文件中添加拦截器。

在mybatis中能够被拦截的类型有四种:

(1)Executor:拦截执行器的方法;

(2)ParameterHandler:拦截参数的处理;

(3)ResultHandler:拦截结果集的处理;

(4)StatementHandler:拦截sql语法构建的处理;

不同类型执行的顺序过程:

3d42b9a1fb853e13d07bd8bc90743378.png

多个插件的拦截顺序:

5c2a6a5a037b9787c72ba4d00d84116c.png

而拦截器对象的处理过程:

db4208016c2c12ca04655debf270a06a.png

多个插件plugin和intercept方法的执行顺序:

先执行每个插件的plugin方法,如果@Intercepts注解表明能够拦截该对象,那么生成类型对象的代理对象。

2.1拦截器注解的规则

具体规则:

@Intercepts({

@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),

@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),

@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})

})

@Intercepts:标识该类是一个拦截器;

@Signature:指明自定义拦截器需要拦截哪一个类型,哪一个方法;

(1) type:对应四种类型中的一种;

(2) method:对应接口中的哪类方法(因为可能存在重载方法);

(3) args:对应哪一个方法;

拦截器可以拦截的方法:

963ed2f14a523134375f7ef2a6cc5cf5.png

2.2拦截器的方法

public interface Interceptor {

Object intercept(Invocation invocation) throws Throwable;

Object plugin(Object target);

void setProperties(Properties properties);

}

2.2.1setProperties

如果我们的拦截器需要一些变量对象,使用方法:

在方法中获取参数:properties.getProperty("username");

2.2.2plugin方法

这个方法的作用,让MyBatis判断,是否要进行拦截,然后决定是否生成一个代理:

@Override

public Object plugin(Object target) {

if (target instanceof StatementHandler) {

return Plugin.wrap(target, this);

}

return target;

}

值得注意的是:每经过一个拦截器对象都会调用插件的plugin方法,也就是说,该方法会调用4次。根据@Intercepts注解来决定是否进行拦截处理。

2.2.3 intercept(Invocation invocation)方法

我们知道,mybatis只能拦截四种类型的对象。而intercept方法便是处理拦截到的对象。比如我们要拦截StatementHandler#query(Statement st,ResultHandler rh)方法,那么Invocation就是这个对象,Invocation中有三个参数。

target:StatementHandler;

method :query;

args[]:Statement st,ResultHandler rh

org.apache.ibatis.reflection.SystemMetaObject#forObject:方便的获取对象中的值。

案例:拼接sql语句:

@Intercepts({

@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),

@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),

@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})

})

例如:

@Slf4j

public class Aa implements Interceptor {

@Override

public Object intercept(Invocation invocation) throws Throwable {

Statement statement;

//获取方法参数

Object firstArg = invocation.getArgs()[0];

if (Proxy.isProxyClass(firstArg.getClass())) {

statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement");

} else {

statement = (Statement) firstArg;

}

MetaObject stmtMetaObj = SystemMetaObject.forObject(statement);

//获取Statement对象(sql语法已经构建完毕)

statement = (Statement) stmtMetaObj.getValue("stmt.statement");

//获取sql语句

String originalSql = statement.toString();

//将原始sql中的空白字符(s包括换行符,制表符,空格符)替换为" "

originalSql = originalSql.replaceAll("[s]+", " ");

//只获取sql的select/update/insert/delete开头的sql

int index = indexOfSqlStart(originalSql);

if (index > 0) {

originalSql = originalSql.substring(index);

}

// 计算执行 SQL 耗时

long start = System.currentTimeMillis();

Object result = invocation.proceed();

long timing = System.currentTimeMillis()- start;

//获取MapperStatement对象,获取到sql的详细信息

Object realTarget = realTarget(invocation.getTarget());

//获取metaObject对象

MetaObject metaObject = SystemMetaObject.forObject(realTarget);

//获取MappedStatement对象

MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");

StringBuilder formatSql = new StringBuilder()

.append(" Time:").append(timing)

//获取Mapper信息和方法信息

.append(" ms - ID:").append(ms.getId())

.append("Execute SQL:")

.append(originalSql);

//打印sql信息

log.info(formatSql.toString());

return result;

}

@Override

public Object plugin(Object target) {

if (target instanceof StatementHandler) {

return Plugin.wrap(target, this);

}

return target;

}

@Override

public void setProperties(Properties prop) {

}

/**

* 获取sql语句开头部分

*

* @param sql

* @return

*/

private int indexOfSqlStart(String sql) {

String upperCaseSql = sql.toUpperCase();

Set set = new HashSet<>();

set.add(upperCaseSql.indexOf("SELECT "));

set.add(upperCaseSql.indexOf("UPDATE "));

set.add(upperCaseSql.indexOf("INSERT "));

set.add(upperCaseSql.indexOf("DELETE "));

set.remove(-1);

if (CollectionUtils.isEmpty(set)) {

return -1;

}

List list = new ArrayList<>(set);

list.sort(Comparator.naturalOrder());

return list.get(0);

}

/**

*

* 获得真正的处理对象,可能多层代理.

*

*/

@SuppressWarnings("unchecked")

public static T realTarget(Object target) {

if (Proxy.isProxyClass(target.getClass())) {

MetaObject metaObject = SystemMetaObject.forObject(target);

return realTarget(metaObject.getValue("h.target"));

}

return (T) target;

}

}

三、拦截器实例

a4b268ee29368e476b8e74a7ba2c4ef3.png
32382e7d4f63d157ab341e81460c63f0.png
3e976c4c1cafb660cf3c83f8ff7a49de.png
908d02efd811ddd0de0129ab7cc83ca1.png
49874c2918c2fd53118d5caebf71a3d1.png
6bf3140087572375b2ea5819d58c4377.png
736324b94fac4633e746f2b920e90057.png

MyBatis配置:

添加拦截器:

93454c32a3b996192243146800ad4c0a.png

整体mybatis配置如下

33badfc63c0f33afffec99c870808f8e.png
f27f1a14ad809e1d702a8a080ffaf58d.png
573be66898f9df17bb9c2259ee381840.png
900ad7f3de5c89c96c313b3ad231a12d.png

这就是MyBatis拦截器的实例

四、实际总结

MyBatis拦截器中还可以获取HttpServletRequest对象,笔者根据获取请求Header头缀中的token,从而获取部门编号和权限类型。然后拦截拼接sql语句完成数据权限过滤。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值