• MyBatis的一二级缓存


    一级缓存

    一级缓存默认是开启的,生命周期和SqlSession相同。一个会话中每次执行一个查询操作时,会先查询二级缓存,如果二级缓存没查到或者二级缓存未开启就会从一级缓存中查询,如果一级缓存也未查到就从数据库中查询

    一级缓存使用条件
    1. 必须是相同的SQL语句
    2. 必须是相同的参数
    3. 必须是同一个会话
    4. 必须是同一个namespace即相同的mapper接口
    5. 必须是同一个mapper接口中的同一个方法
    6. 查询之前没有增删改操作(不管操作是否成功,只要进行了增删改操作就会清空一级缓存)
    7. 查询之前没有执行sqlSession.clearCache()方法

    二级缓存

    二级缓存需要我们手动开启,生命周期和SqlSessionFactory相同。可以适用于多个session之间共享数据

    开启二级缓存
    1. 在mapper接口上面写@CacheNamespace注解,需要注意的是如果采用了注解方式,那么写SQL语句也需要使用注解,否则二级缓存不会生效

      @CacheNamespace(

      ​ implementation = PerpetualCache.class, // 缓存实现 Cache接口 实现类

      ​ eviction = LruCache.class,// 缓存算法

      ​ flushInterval = 60000, // 刷新间隔时间 毫秒

      ​ size = 1024, // 最大缓存引用对象

      ​ readWrite = true, // 是否可写

      ​ blocking = false // 是否阻塞

      )

    2. 在xxxMapper.xml文件中定义cache标签

    二级缓存使用条件
    1. 当会话提交或者关闭时才会填充数据到二级缓存中
    2. 必须是在同一个命名空间下
    3. 必须是相同的statment,即同一个mapper接口的同一个方法
    4. 必须是相同的SQL语句和参数
    5. 如果readWrite=true ,实体类必须实现Serializable 接口
    二级缓存清空条件
    1. 任何一种增删改操作 都会清空整个namespace 中的缓存
    2. 只有修改会话提交之后 才会执行清空操作

    调用链

    1. 顶层api调用

      PersonMapper mapper = session.getMapper(PersonMapper.class);
      Person person1 = mapper.selectPersonById(1);
      
    2. 不管是调用select方法还是update方法,都会进入DefaultSqlSession类中,并调用对应方法

      /*
      DefaultSqlSession > selectList()
      
      getMappedStatement(statement) 方法,statement即为namespace + selectId。通过configuration获取mapperedStatement,MapperedStatement封装了id、sqlSource、resultMap、paramType等。
      
      通过executor调用下一层的executor执行器执行
      */
      
      @Override
        public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
          try {
            MappedStatement ms = configuration.getMappedStatement(statement);
            return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
          } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
          } finally {
            ErrorContext.instance().reset();
          }
        }
      
    3. 然后进入CacheExecutor中尝试获取二级缓存

      /*
      CacheExecutor > query()
      
      获取二级缓存的key值
      */
      @Override
        public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
            
          BoundSql boundSql = ms.getBoundSql(parameterObject);
          CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
          return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        }
      
       /*
       CacheExecutor > query()
       
       尝试获取二级缓存的值
      */
      
      @Override
        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) { //如果cache为空,说明未开启二级缓存
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
              ensureNoOutParams(ms, boundSql);
              @SuppressWarnings("unchecked")
              List<E> list = (List<E>) tcm.getObject(cache, key);
                //如果未从二级缓存获取到对应的值,就走一级缓存
              if (list == null) { 
                list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                  //将值存入二级缓存中
                tcm.putObject(cache, key, list); // issue #578 and #116
              }
              return list;
            }
          }
            //如果没有获取二级缓存,就尝试去获取一级缓存
          return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      
      1. 如果二级缓存没有开启或者未从二级缓存中获取到值,就走一级缓存
      /*
      BaseExecutor > query()
      */
      	......
      	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--;
          }
      
      	......从缓存中获取值
      
    4. 不管是二级缓存,还是一级缓存都是从perpetualCache类中的HashMap中进行取值和赋值等操作

       /*
       PerpetualCache > getObject()
       */
      
        @Override
        public Object getObject(Object key) {
          return cache.get(key);
        }
      
  • 相关阅读:
    五种常见的 PHP 设计模式(收藏)
    写年度工作总结
    关于window.open和window.showdialog返回值的问题
    50个令人叹为观止的JavaScript应用站点[转]
    10大免费FLV播放器下载[转]
    6个去掉图片上的文字的技巧实用简单
    mysql命令大全(转)
    10款替代Windows Media Player的播放器
    Editplus FTP远程访问Ubuntu
    C++ 元编程 Meta Programming
  • 原文地址:https://www.cnblogs.com/zcr-xiaozhai/p/14037141.html
Copyright © 2020-2023  润新知