5個問題讓你明白mybatis一級緩存

一級緩存

主要內容:sql

圖片

一級緩存也叫本地緩存(LocalCache),Mybatis的一級緩存是會話級別(SqlSession)層面進行緩存的。Mybatis的一級緩存是默認開啓的。咱們開發項目中不須要作任何配置,可是若是想關閉一級緩存,能夠使用localCacheScopde=statement來關閉。數據庫

如何關閉一級緩存呢?

在BaseExecutor的中,請看下面代碼:緩存

圖片

爲何說是SqlSession層面緩存?

就是一級緩存的生命週期和一個SqlSession對象的生命週期同樣。mybatis

下面這段中,就會使用到一級緩存。app

SqlSession sqlSession1 = sqlSessionFactory.openSession();
User user1 = sqlSession1.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById"1);
User user2 = sqlSession1.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById"1);

結果輸出:ide

圖片

用兩張圖來總結:ui

第一次:查數據庫,放入到緩存中。this

圖片

第二次:直接從緩存中獲取。url

圖片

下面這段代碼中就使用不到緩存spa

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
sqlSession1 = sqlSessionFactory.openSession();
    
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    
System.out.println("第一次查詢");
System.out.println(userMapper.selectById(1));
    
User user = new User();
user.setUserName("tian111");
user.setId(1);
userMapper1.updateAuthorIfNecessary(user);
System.out.println("第二次查詢");
System.out.println(userMapper.selectById(1));

輸出結果:

圖片

用三張圖來總結:

第一次查詢:sqlSession1查詢數據庫,放入到緩存中。

圖片

更新:sqlSession2進行更新,注意這裏寫入的是sqlSession本身的本地緩存。

圖片

第二次查詢:sqlSession1第二次查詢。

圖片

記住是一級緩存只能是同一個SqlSession對象就好了。

一級緩存維護在哪裏的?

既然一級緩存的生命週期和SqlSession一致,那麼咱們能夠猜測,這個緩存是否是就維護在SqlSession中呢?

SqlSession的默認實現類DefaultSqlSession,在DefaultSqlSession中只有兩個屬性可能存放緩存:

private final Configuration configuration;
private final Executor executor;

configuration是全局的,確定不會放緩存。

那就只能把但願寄託於Executor了。因爲Executor是個接口,咱們能夠看看他的實現類:

圖片

另外這裏有個BaseExecutor。有各種也得瞄瞄。一看竟然有東西。

public abstract class BaseExecutor implements Executor {
  private static final Log log = LogFactory.getLog(BaseExecutor.class);
  protected Transaction transaction;
  protected Executor wrapper;
  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  //熟悉的傢伙,基本緩存
  protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;
  protected Configuration configuration;
    
  protected int queryStack;
  private boolean closed;
    
  protected BaseExecutor(Configuration configuration, Transaction transaction) {
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue<>();
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    this.closed = false;
    this.configuration = configuration;
    this.wrapper = this;
  }
    
}

再看看BaseExecutor類圖:

圖片

因此這就證實了,這個緩存是維護在SqlSession裏。

一級緩存何時被清空?

在執行update、insert、delete、flushCache="true"、commit、rollback、LocalCacheScope.STATEMENT等狀況下,一級緩存就都會被清空。

@Override
public void clearLocalCache() {
    if (!closed) {
      localCache.clear();
      localOutputParameterCache.clear();
    }
}

update時,一級緩存會被清空。delete和insert都是調用這個update。能夠從SqlSession的insert、update、delete方法跟蹤。

圖片

LocalCacheScope.STATEMENT時,一級緩存會被清空。在BaseExecutor裏的query方法中:

圖片

事務提交回滾時,一級緩存會被清空。

圖片


flushCache="true"時,一級緩存會被清空。

圖片

一級緩存key是什麼?

圖片


下面就是一級緩存key的建立過程

@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    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();
    // mimic DefaultParameterHandler logic
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        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 = configuration.newMetaObject(parameterObject);
        value = metaObject.getValue(propertyName);
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
    // issue #176
    cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

id:com.tian.mybatis.mapper.UserMapper.selectById

key的生成策略:id + offset + limit + sql + param value + environment id,這些值都相同,生成的key就相同。

示例:

圖片

一級緩存總結

一級緩存的生命週期和SqlSession對象的生命週期一致。因此緩存維護在SqlSession中的屬性executor裏。

一級緩存默認開啓。能夠經過修改配置項把一級緩存關掉。

清空一級緩存的方式有:

  • update、insert、delete
  • flushCache="true"
  • commit、rollback
  • LocalCacheScope.STATEMENT
相關文章
相關標籤/搜索