乐趣区

关于mybatis:不明白mybatis一级缓存那我用这5个问题讲给你听

一级缓存

次要内容:

一级缓存也叫本地缓存(LocalCache),Mybatis 的一级缓存是会话级别(SqlSession)层面进行缓存的。Mybatis 的一级缓存是默认开启的。咱们开发我的项目中不须要做任何配置,然而如果想敞开一级缓存,能够应用 localCacheScopde=statement 来敞开。

如何敞开一级缓存呢?

在 BaseExecutor 的中,请看上面代码:

为什么说是 SqlSession 层面缓存?

就是一级缓存的生命周期和一个 SqlSession 对象的生命周期一样。

上面这段中,就会应用到一级缓存。

`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);
`

后果输入:

用两张图来总结:

第一次:查数据库,放入到缓存中。

第二次:间接从缓存中获取。

上面这段代码中就应用不到缓存

`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
退出移动版