• Mybatis使用拦截器添加参数


    一、什么是Mybatis拦截器

    Mybatis拦截器是mybatis提供的一套接口,用于拦截mabatis访问数据库时的行为,并允许我们在拦截中,添加自己需要的自定义操作。

    二、 如何使用拦截器添加参数

    先给一段代码:

    @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}),
            @Signature(
                    type = Executor.class,
                    method = "update",
                    args = {MappedStatement.class, Object.class}),
    
            })
    public class MybatisInterceptor implements Interceptor {
        private static final Logger logger = LoggerFactory.getLogger(MybatisInterceptor.class);
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            Object[] args = invocation.getArgs();
            MappedStatement mappedStatements = (MappedStatement) args[0];
            Map<String, Object> paramMap = new MapperMethod.ParamMap<>();
            if (args[1] != null) {
                if (args[1] instanceof MapperMethod.ParamMap) {
                    paramMap.putAll((Map) args[1]);
                } else if (BeanUtils.isSimpleValueType(args[1].getClass())) {
                    String mapperId = mappedStatements.getId();
                    Class<?> paramType = args[1].getClass();
                    String clazzName = mapperId.substring(0, mapperId.lastIndexOf("."));
                    String methodName = mapperId.substring(mapperId.lastIndexOf(".") + 1);
                    Class<?> target = Class.forName(clazzName);
                    Method method = target.getMethod(methodName, paramType);
                    Parameter[] params = method.getParameters();
                    String paramName = params[0].getName();
    
                    paramMap.put(paramName, args[1]);
                } else {
                    List<Field> fields = new ArrayList<>();
                    getAllFields(fields, args[1].getClass());
    
                    for (Field field : fields) {
                        Object fieldValue = null;
                        String fieldName = field.getName();
                        try {
                            fieldValue = (new PropertyDescriptor(fieldName, args[1].getClass())).getReadMethod().invoke(args[1]);
                        } catch (Exception var9) {
                            logger.error(var9.getMessage(), var9);
                        }
                        paramMap.putIfAbsent(field.getName(), fieldValue);
                    }
                }
            }
    
            paramMap.put("key", "value");
            args[1] = paramMap;
            return invocation.proceed();
    
        }
    
        private List<Field> getAllFields(List<Field> fields, Class<?> type) {
            fields.addAll(Arrays.asList(type.getDeclaredFields()));
            if (type.getSuperclass() != null) {
                this.getAllFields(fields, type.getSuperclass());
            }
    
            return fields;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    实现一个拦截器主要有3步。

    1 实现Interceptor接口

    Interceptor接口是mbatis提供的拦截器接口。mybatis会执行该接口声明的方法去拦截对应的操作。

    Interceptor接口有三个方法,一个必须实现的方法,两个使用Interceptor的默认实现就够用的方法。

    1)Object intercept(Invocation var1)

    必须实现的方法,使用该方法在拦截中途添加自己想要的自定义操作。

    2)plugin(Object target)

    可选的方法,将插件添加到mybatis操作中。

    3)setProperties(Properties properties)

    可选的方法,读取配置中的属性。

    2 添加@Intercepts注解

    @Intercepts注解的作用是,标记需要拦截的方法列表。mybatis通过该注解去判断当前方法是否需要被拦截。
    @Intercepts其实就是一个数组,用来添加复数个@Signature
    每个@Signature都指定了一个需要拦截的方法。

    @Signature注解有3个参数。

    1)type

    指定拦截的类的类型,type有4个类型可选
    Executor.classParameterHandler.classResultSetHandler.classStatementHandler.class

    2)method

    指定拦截的方法名

    3)args

    指定方法的参数类型,去对应的方法里看有哪些参数类型就可以了。如果填错会报错。
    mybatis会根据这三个参数找到对应的方法,并进行拦截。
    mybatis给出了允许拦截的方法列表

    Executor (update, query, flushStatements, commit, rollback,getTransaction, close, isClosed)
    ParameterHandler (getParameterObject, setParameters)
    ResultSetHandler (handleResultSets,handleOutputParameters)
    StatementHandler (prepare, parameterize,batch, update, query)

    例子
    @Signature(
            type = Executor.class,
            method = "query",
            args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    
    • 1
    • 2
    • 3
    • 4

    这个注解声明了,要拦截的类是

    Executor.class
    
    • 1

    要拦截的方法名是

    query
    
    • 1

    该方法的参数有4个,类型是

    MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class
    
    • 1

    3 intercept方法详解

    intercept方法用来在拦截时添加自定义的操作。
    该方法提供给我们一个参数:Invocation invocation
    Invocation类有3个字段,

        private final Object target;
        private final Method method;
        private final Object[] args;
    
    • 1
    • 2
    • 3

    target 被拦截的类
    method 被拦截的方法
    args 方法的实际参数

    既然我们要添加参数到拦截的方法里,所以我们需要关心的主要就是这个args
    args是一个参数数组,里面的参数数量对应着@Signature声明的那几个参数,也就是拦截方法的参数。
    举个例子:

    @Signature(
            type = Executor.class,
            method = "query",
            args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    
    • 1
    • 2
    • 3
    • 4

    拦截该方法时,args就是一个长度为4的数组,里面4个参数是

    MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class
    
    • 1

    其中,第二个参数,也就是args[1],是实际执行Mapper时传入的参数,后续sql会根据args[1]里的值来填充sql

    所以我们要做的就是把自己的参数添加到args[1]中。

    添加参数也是有说法的。args[1]是一个Object类型,实际类型会根据Mapper方法的参数数量和类型发生变化。
    因此需要分多种情况考虑。

    1)无参数

    Mapper执行的是一个无参方法时,args[1]null
    需要创建一个MapperMethod.ParamMap对象,然后将自定义的参数添加进去,再赋值给arg[1]就可以了。

    MapperMethod.ParamMap是一个继承于Map的类。把他当作一个Map就可以了。

    2)一个参数

    Mapper执行的是一个单一参数方法时,args[1]Object类型。
    需要创建一个MapperMethod.ParamMap对象,将已经有的参数放进去,然后将自定义参数放进去。
    如果args[1]是一个基本数据类型,就存在一个问题,Mapkey-value结构,args[1]只有值,没有参数名。我们知道value是不够的,sql解析时根据参数名去找对应的value。
    所以我们需要去反射实际执行的mapper,拿到对应的参数名。
    如果args[1]是一个引用类型,我们就需要解析他所有的字段,将字段名和其值组成key-value键值对,存储到Map中。
    因为单一参数是引用时,mybatis会直接忽略参数名,直接匹配参数中的字段名。

    3)多个参数

    Mapper执行的是一个多参数方法时,args[1]已经是MapperMethod.ParamMap类型。
    所以我们无需修改args[1]的类型,直接将自定义的参数添加进去就可以了。

    注意点

    1. @Param注解修饰的参数虽然参数名称发生改变,但无需特别处理,因为@Param注解并非修改源参数的名称,而是添加了一个新的参数。

    三、应用场景

    可以将创建者,更新者,这类通用参数通过拦截器添加到参数中,无需手动添加该参数,也不用写AOP方法。
    可以做分库分表,根据上下文将实际操作的数据库名传入其中,无需手动判断操作的是哪个数据库。

  • 相关阅读:
    Windows AD共享权限管理工具
    前端—— 分层模型和应用协议
    计算机网络重点概念整理-第三章 数据链路层【期末复习|考研复习】
    leetcode 刷题记录
    生命在于学习——Linux提权
    Latex 报错:The font cannot be found.
    【大屏项目】SpringBoot + Vue 实现的可视化拖拽编辑的
    Android 组件化
    【学习笔记】POJ 3834 graph game
    Linux 配置Java环境
  • 原文地址:https://blog.csdn.net/qq_42068856/article/details/126333596