zoukankan      html  css  js  c++  java
  • Mybatis

    缓存

    一级缓存(Local cache)

    • 作用范围:单个SqlSession,与SqlSeesion中的Executor组合在一起(当前SqlSession执行select语句则发生缓存写入,发生update类语句则发生缓存清空)
    • 弊端:不能跨会话共享,无法感知到其他SqlSession发生的update类语句,可能获取到的缓存为过时数据

    二级缓存(namespace cache)

    • 作用范围:单个namespace,通过TransactionalCacheManager对象(全局)管理,只有当事务被commit或rollback,且之后没有update类语句时,才会将缓存写入,而update类语句发生commit则缓存清空
    • tips 1:二级缓存的开启,需要configuration配置中cacheEnabled=true,且Mapper.xml文件中显示定义标签
    • tips 2: 二级缓存的实现,是通过CachingExecutor代理BaseExecutor实现的

    others

    1. 缓存的实现是基于PerpetualCache类的,通过代理的方式实现功能增强,二级缓存默认大小为1024,采用LRU算法
    2. Mybatis提供了对第三方缓存的支持,如:redis(需引入mybatis-redis相关依赖)
      缓存

    插件

    demo(逻辑翻页转换为物理翻页)

    ① 实现Interceptor接口,② 在mybatis-config.xml文件中通过标签进行配置

    // @Intercepts注解中配置需要拦截的对象类型,以及拦截的方法和参数
    @Intercepts(
            {@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    })
    @Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    })
    public class MyPageInterceptor implements Interceptor {
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            // 将逻辑分页(全部查询出来抛弃多余数据)处理成物理翻页(limit字段定向查询)
            Object[] args = invocation.getArgs();
            MappedStatement ms = (MappedStatement)args[0];   // MappedStatement
            BoundSql boundSql = ms.getBoundSql(args[1]);     // Object parameter
            RowBounds rb = (RowBounds)args[2]; // RowBounds
    
            // 1. 若RowBounds为空,无需分页
            if (rb == RowBounds.DEFAULT) {
                return invocation.proceed();
            }
    
            // 否则 - 重置RowBounds参数对象,将翻页设置取消
            args[2] = RowBounds.DEFAULT;
    
            // 并在SQL后加上limit语句
            String sql = boundSql.getSql();
            String limit = String.format("LIMIT %d,%d", rb.getOffset(), rb.getLimit());
            sql = sql + " " + limit;
    
            // 重构SqlSource,置换原来的SqlSource
            SqlSource sqlSource = new StaticSqlSource(ms.getConfiguration(), sql, boundSql.getParameterMappings());
            // 通过反射修改参数
            Field field = MappedStatement.class.getDeclaredField("sqlSource");
            field.setAccessible(true);
            field.set(ms, sqlSource);
    
            // 执行被拦截方法
            return invocation.proceed();
        }
    }
    

    插件原理

    解析:SqlSessionFactoryBuilder对象解析配置文件时,会将对应的插件类注册到configration对象的InterceptorChain
    创建:获取sqlSession时,对于四大对象,会调用InterceptorChain.pluginAll(target)方法,通过代理的方式,实现intercept(逐层代理形成intercept链)

    调用:
    ①代理对象被拦截的方法 -> ②plugins.invoke()方法
    -> ③invoke内部调用Interceptor.intercept(Invocation invocation)方法,执行拦截逻辑
    -> ④调用invocation.proceed()进入下一层逻辑

    mybatis 代理逻辑

    // Plugin对象
    public class Plugin implements InvocationHandler {
    
      private final Object target;
      private final Interceptor interceptor;
      private final Map<Class<?>, Set<Method>> signatureMap;
    
      private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
        this.target = target;
        this.interceptor = interceptor;
        this.signatureMap = signatureMap;
      }
    
      public static Object wrap(Object target, Interceptor interceptor) {
        // 构建代理对象:通过对象类型和方法签名获取到接口,为该接口生成代理类,以Plugin对象为invokeHandler
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        Class<?> type = target.getClass();
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        if (interfaces.length > 0) {
          return Proxy.newProxyInstance(
              type.getClassLoader(),
              interfaces,
              new Plugin(target, interceptor, signatureMap));
        }
        return target;
      }
    
      // 调用step2:通过代理对象方法进入到InvocationHandler类的invoke方法,再调用interceptor.intercept()实现插件逻辑
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          Set<Method> methods = signatureMap.get(method.getDeclaringClass());
          if (methods != null && methods.contains(method)) {
            return interceptor.intercept(new Invocation(target, method, args));
          }
          return method.invoke(target, args);
        } catch (Exception e) {
          throw ExceptionUtil.unwrapThrowable(e);
        }
      }
    ...
    }
    
    // Invocation对象
    public class Invocation {
    
      private final Object target;
      private final Method method;
      private final Object[] args;
    
      public Invocation(Object target, Method method, Object[] args) {
        this.target = target;
        this.method = method;
        this.args = args;
      }
    
      public Object getTarget() { return target;}
      public Method getMethod() { return method;}
      public Object[] getArgs() { return args;}
    
      public Object proceed() throws InvocationTargetException, IllegalAccessException {
        return method.invoke(target, args);
      }
    }
    

    tips 1: mybatis通过将Plugin与Interceptor对象组合,将InvocationHandler.invoke()方法中的细节实现抽取到Interceptor.intercept()中(桥接模式,使控制维度与实现维度解耦)
    tips 2: mybatis插件的代理顺序与mybatis-config.xml的定义顺序一致,而插件的执行顺序将从最外层代理往里执行(先执行最后定义的插件逻辑)

    四大对象(可被拦截的)

    Executor:update, query, flushStatements, commit, rollback, getTransaction, close, isClosed

    • ExecutorType.SIMPLE:普通执行器,为每个语句的执行创建一个新的预处理语句
    • ExecutorType.REUSE:statement缓存,该类型的执行器会复用预处理语句
    • ExecutorType.BATCH:批量执行器,该类型的执行器会批量执行所有更新语句
      ParameterHandler:getParameterObject, setParameters
      ResultSetHandler:handleResultSets, handleOutputParameters
      StatementHandler:prepare, parameterize, batch, update, query

    欢迎疑问、期待评论、感谢指点 -- kiqi,愿同您为友

    -- 星河有灿灿,愿与之辉

  • 相关阅读:
    leetcode 155. Min Stack 、232. Implement Queue using Stacks 、225. Implement Stack using Queues
    leetcode 557. Reverse Words in a String III 、151. Reverse Words in a String
    leetcode 153. Find Minimum in Rotated Sorted Array 、154. Find Minimum in Rotated Sorted Array II 、33. Search in Rotated Sorted Array 、81. Search in Rotated Sorted Array II 、704. Binary Search
    leetcode 344. Reverse String 、541. Reverse String II 、796. Rotate String
    leetcode 162. Find Peak Element
    leetcode 88. Merge Sorted Array
    leetcode 74. Search a 2D Matrix 、240. Search a 2D Matrix II
    Android的API版本和名称对应关系
    spring 定时任务执行两次解决办法
    解析字符串为泛型的方法
  • 原文地址:https://www.cnblogs.com/kiqi/p/14368712.html
Copyright © 2011-2022 走看看