一、动态改变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 2021-09-19 17:25  格物致知_Tony  阅读(247)  评论(0)    收藏  举报