mybatis的缓存相关源码分析

目录

前言

一、mybatis中的缓存相关源码分析

1.二级缓存

2.一级缓存

二、mybatis与spring结合时的一级缓存失效问题

总结


前言

mybatis中的缓存分为一级缓存和二级缓存,在查询相同内容的时候能够提高查询速度。本文从源码上分析缓存的实现以及mybatis和spring结合之后一级缓存失效的问题。


一、mybatis中的缓存相关源码分析

1.二级缓存

在查询过程中,首先在二级缓存中查找,当二级缓存中找不到时继续接下来的查询,源码如下,可以看出二级缓存是mapperStatement对象中的。

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    /*
      mybatis的二级缓存
     */
    Cache cache = ms.getCache();
    if (cache != null) {
      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);
  }

2.一级缓存

在二级缓存中无法查找到当前内容时,会 在一级缓存中查找,之后会在数据库中查找,相关代码如下,二级缓存是baseExecutor中一个属性,每个sqlsession都有自己的baseExecutor,所以当sqlsession不同时,一级缓存会失效。

  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++;
      /*
        去localCache本地缓存中查,一级缓存
       */
      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--;
    }
    return list;
  }

二、mybatis与spring结合时的一级缓存失效问题

上面说到sqlsession不同会导致缓存失效,这一块内容在spring与mybatis的结合中也有体现。

  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       //获取sqlsession
      SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
    ......
    }
  }
  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
    //在TransactionSynchronizationManager中获取session
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    LOGGER.debug(() -> "Creating a new SqlSession");
    //获取不到session时新创建session
    session = sessionFactory.openSession(executorType);
    //如果存在事务,将当前的sessionFactory作为key注册到TransactionSynchronizationManager中
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }

       当存在事获取sqlsession时会首先尝试从TransactionSynchronizationmanager中获取,当获取不到时就会重新创建,当存在事物的时候,会将当前的sqlsession注册到TransactionSynchronizationManager中,所以存在事务的时候一级缓存不会失效。

        当最近项目组有人在一个事务方法中连续进行了三次同样的查询,第三次查询之后修改结果的值,执行了插入操作,但是对象的修改导致前两次查询的结果也发生了变化。这里就是使用到了一级缓存,三次查询产生的对象是同一个,相当于三个变量指向同一个对象,修改之后都会改变。这里我建议将数据查出来,然后执行插入操作,可以新建一个对象,避免影响其他值。


总结

        本文主要分析了mybatis中的缓存相关原理,一级spring结合mybatis时缓存失效的原因,但spring为啥会考虑在有事务的情况下使用同一个sqlsession等相关问题有待分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值