随笔 - 1162  文章 - 0  评论 - 16  阅读 - 59万 

一、动态改变SQL运行的参数

  我们可以在目标方法放行前后,做非常多的事情,以到达动态修改 MyBatis 的运行流程。

  在上面的插件开发基础上,当我们测试要查询id为1号的员工时,实际从数据库查询3号员工。

  插件的实现:

复制代码
/**
 * 完成插件签名
 *      告诉MyBatis当前插件用用来拦截哪个对象的哪个方法
 */
@Intercepts(
        {
                @Signature(type = StatementHandler.class, method = "parameterize", args = java.sql.Statement.class)
        }
)
public class MyFirstPlugin implements Interceptor {

    /**
     * intercept:拦截
     *      拦截目标对象的目标方法的执行。
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("MyFirstPlugin...intercept:" + invocation.getMethod());

        //动态的改变一下SQL运行的参数,以前查询1号员工,实际从数据库查询3号员工
        System.out.println("当前拦截到的对象:" + invocation.getTarget());
        //拿到 PreparedStatementHandler ===> ParameterHandler ===>parameterObject
        Object target = invocation.getTarget();
        // 拿到target的元数据
        MetaObject metaObject = SystemMetaObject.forObject(target);
        Object value = metaObject.getValue("parameterHandler.parameterObject");
        System.out.println("sql 语句用的参数是:" + value);
        //修改完SQL语句要用的参数
        metaObject.setValue("parameterHandler.parameterObject", "3");


        //执行目标方法
        Object proceed = invocation.proceed();

        //返回执行后的返回值
        return proceed;
    }

    /**
     * plugin:包装
     *      包装目标对象的;包装,为目标对象创建一个代理对象
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        System.out.println("MyFirstPlugin...plugin:MyBatis将要包装的对象" + target);
        //借助Plugin的 wrap 方法来使用当前Interceptor 包装我们目标对象
        Object wrap = Plugin.wrap(target, this);
        //返回当前 target 创建的动态代理
        return wrap;
    }

    /**
     * setProperties:
     *      将插件注册时 property 属性设置进来
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息" + properties);
    }
}
复制代码

 

  测试代码:

复制代码
     @Test
     public void test1() throws IOException {
          //1、获取 sqlSessionFactory
          SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();

          //2、获取 sqlSession 实例,能直接执行已经映射的 SQL 语句
          SqlSession sqlSession = sqlSessionFactory.openSession();

          try {
               //3、获取接口的实现类对象
               /**
                * 推荐使用
                * 会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
                */
               EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
               Employee emp = employeeMapper.getEmpById(1);
               System.out.println(emp);
          } finally {
               sqlSession.close();
          }
     }
复制代码

 

 

  运行结果:

 

 

   可以看到在测试程序中传入的参数是1,我们在插件中手动修改为了 3,MyBatis在传递参数的时候是插件中的值,而且也生效了。

  

  分析:如何修改参数的?

  ① 首先来看 StatementHandler 接口中的 parameterize() 方法,这是我们要拦截的方法,这也是来给参数赋值的方法;

  

 

  ② 然后是到 RoutingStatementHandler 里面的 方法,具体是由 delegate 来执行的:

  

   

 

     在它的构造器里面来给 delegate 赋值,我们用的默认都是 PREPARED,

  ③ 再看 PreparedStatementHandler 的 parameterize() 方法

  

 

     可以看到具体调用的是 parameterHandler的方法。

 

  ④ ParameterHandler 接口中的方法:

    

 

  ⑤ 默认的是实现是 DefaultParameterHandler 类:

    

 

     在ParameterHandler 中有以下的成员变量信息,

    

 

    获取如果我们要修改参数的时候,直接获取 paramterObject 对象,然后再重新赋值即可。

 

 

 

  注意:在使用插件时,一定要谨慎修改,因为这里可以触及到 MyBatis 的底层原理。

 

posted on   格物致知_Tony  阅读(240)  评论(0编辑  收藏  举报
编辑推荐:
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?
历史上的今天:
2019-09-19 数组模拟队列
2019-09-19 队列
点击右上角即可分享
微信分享提示

目录导航