mybatis 自定义插件

前言

在 mybatis 中自写插件,需要先了解SqlSession下的四大对象、Interceptor接口以及工具类MetaObject 。

SqlSession下的四大对象

Mybatis 根据映射器的 XML 文件的命名空间与接口的全路径相对应,sql与方法绑定起来,通过动态代理,让接口跑起来。然后采用命令模式,使用SqlSession接口的方法使它能够执行查询。




Mapper 执行的过程是通过Executor、StatementHandler、ParameterHandler和ResultHandler来完成数据库操作和结果返回。

  • Executor 代表执行器,由它来调度 StatementHandler、ParameterHandler和ResultHandler等来执行对应SQL。
  • StatementHandler 的作用是使用数据库的 Statement(PreparedStatement)执行操作。
  • ParameterHandler 用于SQL 对参数的处理。
  • ResultHandler 是进行最后数据集(ResultSet)的封装处理。

Interceptor 接口

public interface Interceptor {
    Object intercept(Invocation var1) throws Throwable;

    Object plugin(Object target);

    void setProperties(Properties var1);
}
  • intercept 方法 :它将直接覆盖所拦截对象原有的方法,是插件的核心方法。参数为 Invocation 对象,通过它可以反射调用原来对象的方法。
  • plugin 方法 : target 是被拦截对象,它的作用是给被拦截对象生成一个代理对象,并返回它。
  • setProperties : 允许在 plugin 元素中配置所需参数,方法在插件初始化的时候就被调用了一次。

工具类MetaObject

在MyBatis中,四大对象给我们提供的 public 设置属性的方法很少,我们很难通过对象自身去操作相关的属性信息。但是有了 MetaObject 这个工具类我们就可以去读取或者修改四大对象的属性。

常用的方法有:

  • SystemMetaObject.forObject(Object obj) : 获取MetaObject对象。
  • Object getValue(String name) : 获取对象属性值。
  • void setValue(String name, Object value) : 修改属性值。

玩具插件

功能:打印出要执行Sql的参数,并可对参数进行相关自定义修改。

@Intercepts({@Signature(type = StatementHandler.class, // 有四大对象,这里确定拦截的对象
        method = "prepare", // 确定要拦截的方法  参数的改变要在prepare
        args = {Connection.class, Integer.class} // 要拦截方法的参数
)})
public class ModifyParametersPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 取出被拦截对象
        StatementHandler stmtHandler = (StatementHandler) invocation.getTarget();
        // 由于四大对象public的属性方法很少,MetaObject提供给我们特殊方法去修改四大对象的相关属性
        MetaObject metaStmtHandler = SystemMetaObject.forObject(stmtHandler);
        // 分离代理对象,从而形成多次代理,通过两次循环最原始的被代理类,mybatis使用的是JDK代理
        while (metaStmtHandler.hasGetter("h")) {
            Object object = metaStmtHandler.getValue("h");
            metaStmtHandler = SystemMetaObject.forObject(object);
        }
        // 分离最后一个代理对象的目标类
        while (metaStmtHandler.hasGetter("target")) {
            Object object = metaStmtHandler.getValue("target");
            metaStmtHandler = SystemMetaObject.forObject(object);
        }
        BoundSql boundSql = (BoundSql) metaStmtHandler.getValue("delegate.boundSql");

        Object parameterObject = boundSql.getParameterObject();
        System.out.println("parameterObject: " + parameterObject.toString());
        List parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); ++i) {
                ParameterMapping parameterMapping = (ParameterMapping) parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    // 在parameterMapping中获取属性的名字
                    String propertyName = parameterMapping.getProperty();
                    Object value;
                    // 然后通过MetaObject 对设置 属性的值
                    MetaObject metaParameterObject = SystemMetaObject.forObject(parameterObject);
                    value = metaParameterObject.getValue(propertyName);
                    String sql = (String) metaStmtHandler.getValue("delegate.boundSql.sql");
                    // 此处可以对value进行各种操作,然后在重新赋值
                    if (sql.toLowerCase().contains(" like ")){
                        value = EscapeUtil.escapeChar(value.toString());
                    }
                    // 这个插件对mybatis的级联查询产生了冲突
                    metaParameterObject.setValue(propertyName,value);
                    System.out.println("name:" + propertyName);
                    System.out.println("value:" + value);
                }
            }
        }

        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {

    }
}

你可能感兴趣的:(java,数据库,开发语言)