• mybatis-拦截器实际应用-替换表名-2022新项目


    一、业务场景

      考虑到新项目中部分与业务数据相关的表在后期数据量会比较大,架构师在最开始设计项目中与业务数据相关的表时,就已经考虑使用分表来

    进行处理,给业务数据相关的每张表都添加统一批次的后缀,查询这些数据时,根据不同表名的后缀和来查询对应的数据信息。如果能够动态的

    更改数据表,比如将ai_user表更改为ai_user_20220001,这样就可以动态的查询不同表中的数据。

     

    二、需求分析

      最开始考虑使用的是在xml文件中使用if来做条件判断,根据传入参数的不同来动态查询不同的表。这种方式最开始的时候也没什么问题,只是

    当需要查询的表非常多的时候,需要写的判断语句也会同样的增多,如果需要改动,则非常不便于进行统一处理。就好比是鉴权,如果在每个请求

    的方法中都去写一段鉴权的代码,如果需要进行改动这段代码,那改起来头都大啦。写了几个xml文件后,自己就在考虑能不能像java代码一样,写

    一个拦截器之类的,进行统一处理呢?

    三、解决方案

      先确定一个大方向,方向确定后就开始去找解决方案,去网上搜索相关的内容,学习资料,查看各种博文等等,需要快速学习,并且实际使用。

    这个拦截器需要能够获取到之前的旧有的执行SQL语句,还需要能够获取到执行语句传入的参数,然后根据传入的参数来动态的修改表名,让其生成

    新的SQL语句,最后让拦截器执行新的SQL语句即可。这种方式有些类似于PageHelper插件的处理方式,如果改插件检测到需要分页查询的SQL语句,

    在就是使用拦截器进行处理,更改需要查询的SQL语句,最终完成分页的查询。

      然后开始不断地探索、尝试,同事最后找到适合的解决方案。示例代码如下,由于是内网开发拿不到源码,只写了示例代码来进行讲解说明.

    package mybatis.interceptor;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.plugin.*;
    import org.apache.ibatis.reflection.DefaultReflectorFactory;
    import org.apache.ibatis.reflection.MetaObject;
    import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
    import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
    import org.springframework.stereotype.Component;

    import java.sql.Connection;
    import java.util.Map;
    import java.util.Properties;

    /**
    * @Author yilang
    * @Description TODO
    * @Date 2022/7/7 10:29
    * @Version 1.0
    */

    @Component
    @Intercepts({@Signature(type=StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
    public class MyInterceptor implements Interceptor {

    private final static String SOURCE_TABLE = "app_gift_info";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
    MetaObject metaObject = MetaObject.forObject(statementHandler, new DefaultObjectFactory(),
    new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
    BoundSql boundSql = (BoundSql)metaObject.getValue("delegate.boundSql");
    // 获取执行的SQL
    String sql = boundSql.getSql();
    // 获取执行的SQL参数
    //MapperMethod.ParamMap raramMap = (MapperMethod.ParamMap) boundSql.getParameterObject();
    //Object param = raramMap.get("param");
    Object parameterObject = boundSql.getParameterObject();
    String s = JSON.toJSONString(parameterObject);
    Map map = JSONObject.parseObject(s, Map.class);

    // 替换表名
    //String oldTable = (String)map.get("newTable");
    Object tempTable = map.get("newTablea");
    if (tempTable == null){
    tempTable = "";
    }
    String newTable = (String)tempTable;
    if (sql.contains(SOURCE_TABLE)) {
    sql = sql.replaceAll(SOURCE_TABLE, newTable);
    }
    // 替换执行的的SQL.
    metaObject.setValue("delegate.boundSql.sql", sql);

    return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
    return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
    }

    说明: @Component 注解:不用多说,用来将当前的自定义Mybatis拦截器注册到Spring容器中,让其进行统一管理。

     @Intercepts注解:其value为Signature类数值,注解在Interceptor实现类上,表示实现类对哪些sql执行类(实现Executor)的哪些方法切入

    @Signature:表示一个唯一的Interceptor实现类的一个方法,以及入参.可参考这篇文章 https://zhuanlan.zhihu.com/p/286476884

     

    经过反反复复地调试,最终实现自己想要的功能,能够获取到执行的SQL和传入的参数,然后替换SQL中的表,最后执行新的SQL语句。

    最后测试发现完全可行。

    参考文章-

    https://blog.csdn.net/u011625492/article/details/78426628

    https://zhuanlan.zhihu.com/p/345438831

    https://www.cnblogs.com/blueSkyline/p/10178992.html

     

    测试过程中遇到的问题:在获取SQL执行参数时,如果某个执行方法中只有一个参数,并且没有添加@Param注解时,则转换为map集合会

    报类转换异常的错误,这个需要注意。其他参数则可以根据自己的需要进行更改。

     

    拓展:以前的项目中有一个需求,项目需要记录所有执行的SQL语句。这个功能是其他同事的做的,那时自己也很好奇如何完成的,如何实现

    的,现在使用了Mybatis的拦截器之后,也明白他是如何处理的啦。只需要在上面的语句中做简单的处理即可实现,非常方便。

  • 相关阅读:
    洛谷P1331 海战 题解
    智能井盖:把好城市地下“安全门”
    Java项目:SSM学生会管理系统
    Go 上下文 context.Context
    C++下基于模拟退火算法解决TSP问题
    C和指针 第10章 结构和联合 10.1 结构基础知识
    VSCode设置中文语言界面(VScode设置其他语言界面)
    五、Maven-单一架构案例(业务功能:批复奏折,业务功能:登录检查,打包部署)
    Python---类的定义和使用语法
    Vue中的计算属性和方法有什么区别?
  • 原文地址:https://www.cnblogs.com/yilangcode/p/16482816.html