zoukankan      html  css  js  c++  java
  • mybatis缓存

    使用的mybatis版本3.2.8,也是源码来源

    mybatis对缓存支持情况如下:

    1).一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。

    2).二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。

    3).对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear

    默认下使用一级缓存,要开启二级缓存,可以在mybatis-config.xml中

    <settings>  
        <setting name="cacheEnabled" value="true"/>   
    </settings> 

    或者在mapper接口对应的xml文件中

    <cache/> 

    1.源码剖析

    1.1 对mapper接口的调用会调用MapperProxy<T>

    //当调用 Mapper 所有的方法时,将都交由Proxy 中的 invoke 处理
    public
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args);//执行 mapper method,返回执行结果 } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; }

    1.2 MapperMethod execute执行代码如下:

    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        if (SqlCommandType.INSERT == command.getType()) {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = rowCountResult(sqlSession.insert(command.getName(), param));
        } else if (SqlCommandType.UPDATE == command.getType()) {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = rowCountResult(sqlSession.update(command.getName(), param));
        } else if (SqlCommandType.DELETE == command.getType()) {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = rowCountResult(sqlSession.delete(command.getName(), param));
        } else if (SqlCommandType.SELECT == command.getType()) {
          if (method.returnsVoid() && method.hasResultHandler()) {
            executeWithResultHandler(sqlSession, args);
            result = null;
          } else if (method.returnsMany()) {
            result = executeForMany(sqlSession, args);
          } else if (method.returnsMap()) {
            result = executeForMap(sqlSession, args);
          } else {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(command.getName(), param);
          }
        } else {
          throw new BindingException("Unknown execution method for: " + command.getName());
        }
        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
          throw new BindingException("Mapper method '" + command.getName() 
              + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
        }
        return result;
      }

    1.3 不管是selectOne还是selectMap都要调用DefaultSqlSession中的selectList,代码如下:

    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          MappedStatement ms = configuration.getMappedStatement(statement);
    //如果启动用了Cache 才调用 CachingExecutor.query,反之则使用 BaseExcutor.query 进行数据库查询    List
    <E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); return result; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }

    DefaultSqlSession中的executor是由Configuration创建的,因为Configuration中cacheEnabled默认是true,因此这里的executor是CachingExecutor 

    Executor类图关系如下:

    1.4 CachingExecutor query代码

    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
          throws SQLException {
        Cache cache = ms.getCache();// 获取二级缓存实例
        if (cache != null) {
          flushCacheIfRequired(ms);
          if (ms.isUseCache() && resultHandler == null) {// 当前 Statement 是否启用了二级缓存
            ensureNoOutParams(ms, parameterObject, boundSql);
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) {// 未找到缓存,很委托给 BaseExecutor 执行查询 
              list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
              tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
            }
            return list;
          }
        }
        return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }

    如果没有二级缓存,就调用
    BaseExecutor query方法代码

    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (closed) throw new ExecutorException("Executor was closed.");
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
          clearLocalCache();
        }
        List<E> list;
        try {
          queryStack++;
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {//一级缓存找到
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {//一级缓存没有,查数据库
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        if (queryStack == 0) {
          for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
          }
          deferredLoads.clear(); // issue #601
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            clearLocalCache(); // issue #482
          }
        }
        return list;
      }

    总结大体流程:

    从二级缓存中进行查询 -> [如果缓存中没有,委托给 BaseExecutor] -> 进入一级缓存中查询 -> [如果也没有] -> 则执行 JDBC 查询

    看到这我一直有个问题

    CachingExecutor query方法中使用二级缓存是在MappedStatement中取得的,那MappedStatement又是怎么来的呢?留到下篇

    参考文章:

    1. MyBatis 缓存机制深度解剖 / 自定义二级缓存

    2. MyBatis+Spring基于接口编程的原理分析

  • 相关阅读:
    PID控制算法原理(抛弃公式,从本质上真正理解PID控制)(转)
    用三张图片详解Asp.Net 全生命周期
    Maven 3 入门 核心概念
    Maven 3 入门 HelloWorld
    Spring 3.x MVC 入门3 使用内容协商来实现多视图
    Nosql之Mongodb 1 安装配置与基本操作
    Spring 3.x MVC 入门31 使用内容协商来实现多视图 示例
    Nosql之Mongodb 2 高级查询
    Maven 3 入门 如何创建一个web应用程序
    Spring 3.x MVC 入门4 @ResponseBody & @RequestBody
  • 原文地址:https://www.cnblogs.com/yhzh/p/5287118.html
Copyright © 2011-2022 走看看