欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

当然可以,以下是: 七、Mybatis入门级缓存详解 - 一级缓存说明

最编程 2024-02-19 10:12:57
...

操作:

  1. 在一个sqlSession中,对User表根据id进行两次查询,查看他们发出sql语句的情况
     @Test
    public void firstLevelTest(){
        //第一次查询,发出sql语句,并将查询出来的结果放进缓存中
        User u1 = userMapper.findUserById(1);
        System.out.println(u1);
        //第二次查询,由于是同⼀个sqlSession,会在缓存中查询结果
        //如果有,则直接从缓存中取出来,不和数据库进行交互
        User u2 = userMapper.findUserById(1);
        System.out.println(u2);
        System.out.println(u1==u2);
        sqlSession.close();
    }
  1. 同样是对user表进行两次查询,两次查询之间进行一次update操作
    @Test
    public void firstLevelTest(){
        //第一次查询,发出sql语句,并将查询出来的结果放进缓存中
        User u1 = userMapper.findUserById(1);
        System.out.println(u1);

        User user = new User();
        user.setId(1);
        user.setUsername("cookie");
        user.setPassword("123");
        userMapper.updateUser(user);
        sqlSession.commit();
        sqlSession.clearCache();

        //第二次查询,由于是同⼀个sqlSession,会在缓存中查询结果
        //如果有,则直接从缓存中取出来,不和数据库进⾏交互
        User u2 = userMapper.findUserById(1);
        System.out.println(u2);
        System.out.println(u1==u2);
        sqlSession.close();
    }

总结:

  1. 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中
  2. 如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的 一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
  3. 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息

image.png

1.1 一级缓存原理探究与源码分析

直接从SqlSession看看有没有创建缓存或者与缓存有关的属性或者方法
image.png
上述所有方法中,好像只有clearCache()和缓存沾点关系,直接从这个方法入手,分析源码时,要看它(此类)是谁,它的父类和子类分别又是谁,对如上关系了解了,才会对这个类有更深的认识,分析后得到如下这个流程图**:
image.png
流程走到Perpetualcache中的clear()方法之后,会调用其cache.clear()方法。点进去发现,cache其实就是private Map cache = new HashMap();也就是一个Map,所以说cache.clear()其实就是map.clear(),缓存其实就是本地存放的一个map对象,每⼀个SqISession都会存放⼀个map对象的引用。
最有可能创建缓存的地方是
Executor**,因为Executor是执行器,用来执行SQL请求,而且清除缓存的方法也在Executor中执行,所以很可能缓存的创建也很有可能在Executor中,Executor中有一个createCacheKey方法,createCacheKey方法是由BaseExecutor执行的。

    public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
        if (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            CacheKey cacheKey = new CacheKey();
            cacheKey.update(ms.getId());
            cacheKey.update(rowBounds.getOffset());
            cacheKey.update(rowBounds.getLimit());
            cacheKey.update(boundSql.getSql());
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
            Iterator var8 = parameterMappings.iterator();

            while(var8.hasNext()) {
                ParameterMapping parameterMapping = (ParameterMapping)var8.next();
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    String propertyName = parameterMapping.getProperty();
                    Object value;
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }

                    cacheKey.update(value);
                }
            }

            if (this.configuration.getEnvironment() != null) {
                cacheKey.update(this.configuration.getEnvironment().getId());
            }

            return cacheKey;
        }
    }

创建缓存key会经过⼀系列的update方法,udate方法由一个CacheKey这个对象来执行的,这个 update方法最终由updateList的list来把五个值存进去:
image.png
最后⼀个值,configuration.getEnvironment().getId()是定义在mybatis-config.xml中的标签。

    <environments default="development">
        <environment id="development">
            <!--当前事务交给JDBC处理-->
            <transactionManager type="JDBC"/>
            <!--当前使用mybatis提供的连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

创建完缓存使用:Executor的query方法

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    //创建缓存
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }
  @SuppressWarnings("unchecked")
  @Override
  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();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

先查缓存,缓存没有查数据库并放入缓存中。