Mybatis源码系列5-二级缓存(下)

简介: Mybatis源码系列5-二级缓存(下)

CachingExecutor 执行器内部创建一个TransactionalCacheManager 事务缓存管理,并使用delegate 指向基础Executor


public class CachingExecutor implements Executor {
 //目标Executor
  private Executor delegate;
  private TransactionalCacheManager tcm = new TransactionalCacheManager();
  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }
}


当开启二级缓存的情况下执行sqlsession的select方法时,首先会执行CachingExecutor的query方法。


public <E> List<E> query(...) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(...);
  }
 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) {
      flushCacheIfRequired(ms);//是否清空缓存
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
           //先查询二级缓存
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
        //二级缓存没有,交给基础执行器Executor 去执行查询操作
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          //放入预缓存中。
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }


流程:

  • 先获取二级缓存。
  • 判断当前SQL是否开启二级缓存。
  • 开启的情况下,先去二级缓存查询。
  • 二级缓存有,直接返回
  • 二级缓存没有,交给基础执行器,走一级缓存执行过程。并把直接结果放入事务预缓存区。


事务型预缓存

在二级没有数据的情况下,通过BaseExecutor从数据库中查询到结果后,并没有直接放入二级缓存。而是先放入的事务预缓存中。

tcm.putObject(cache, key, list);

来看看这个预缓存区,如何工作。


//事务缓存管理者
public class TransactionalCacheManager {
private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();
public void putObject(Cache cache, CacheKey key, Object value) {
    getTransactionalCache(cache).putObject(key, value);
}
private TransactionalCache getTransactionalCache(Cache cache) {
    TransactionalCache txCache = transactionalCaches.get(cache);
    if (txCache == null) {
      txCache = new TransactionalCache(cache);
      transactionalCaches.put(cache, txCache);
    }
    return txCache;
  }
}
//事务预缓存
public class TransactionalCache implements Cache {
  private Cache delegate;
  private boolean clearOnCommit;
  private Map<Object, Object> entriesToAddOnCommit;
  private Set<Object> entriesMissedInCache;
  public void putObject(Object key, Object object) {
    entriesToAddOnCommit.put(key, object);
  }
}

事务型缓存TransactionalCache,也可以理解为预缓存区,是通过装饰器模式设计的预缓存,通过delegate属性指向二级缓存,他使得二级缓存具有事务特性。


TransactionalCache 由TransactionalCacheManager事务缓存管理者,进行统一管理。

工作原理:


List<E> list = (List<E>) tcm.getObject(cache, key);
//获取当前key在二级缓存是否对应数据
 public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }
  //获取事务预缓存。
 private TransactionalCache getTransactionalCache(Cache cache) {
    TransactionalCache txCache = transactionalCaches.get(cache);
    if (txCache == null) {
      txCache = new TransactionalCache(cache);
      transactionalCaches.put(cache, txCache);
    }
    return txCache;
  }

查询过程

  • 通过TransactionalCacheManager 查询二级缓存中是否由当前key对应的缓存数据。
  • TransactionalCacheManager 首先会检查当前当前二级缓存是否被事务缓存TransactionalCache装饰,如果没有装饰,就创建一个TransactionalCache装饰一下二级缓存。
  • TransactionalCache#getObject(Key)方法会去二级缓存中查询。

缓存过程:

//TransactionalCacheManager
public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
  }
//TransactionalCache
public void commit() {
    if (clearOnCommit) {
      delegate.clear();
    }
    flushPendingEntries();//刷到二级缓存中
    reset();//清空预缓存
  }
  • 通过TransactionalCacheManager#putObject 方法把从数据查询的结果放入TransactionalCache预缓存中。
  • 当Sqlsession执行commit时,执行TransactionalCacheManager#commit方法,把当前预缓存中的数据正式提交到二级缓存中。并清空预缓存区。

小结:

二级缓存的工作原理: 一个缓存执行器 +   一个预缓存 +      二级缓存


二级缓存的刷新


insert、update、delete操作后都会引发二级缓存的刷新

public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    flushCacheIfRequired(ms);//刷新二级缓存
    return delegate.update(ms, parameterObject);
  }
private void flushCacheIfRequired(MappedStatement ms) {
    Cache cache = ms.getCache();
    if (cache != null && ms.isFlushCacheRequired()) {      
      tcm.clear(cache);//清空二级缓存
    }
  }


总结:


在二级缓存的设计上,MyBatis大量地运用了装饰者模式,如CachingExecutor, 以及各种Cache接口的装饰器。

  • 二级缓存实现了Sqlsession之间的缓存数据共享,属于namespace级别
  • 二级缓存具有丰富的缓存策略。
  • 二级缓存可由多个装饰器,与基础缓存组合而成
  • 二级缓存工作由 一个缓存装饰执行器CachingExecutor和 一个事务型预缓存TransactionalCache 完成。

如果本文任何错误,请批评指教,不胜感激 !

如果觉得文章不错,点个赞


相关文章
|
7天前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
3月前
|
SQL 缓存 Java
MYBATIS缓存
MYBATIS缓存
|
1月前
|
XML Java 数据库连接
mybatis源码研究、搭建mybatis源码运行的环境
这篇文章详细介绍了如何搭建MyBatis源码运行的环境,包括创建Maven项目、导入源码、添加代码、Debug运行研究源码,并提供了解决常见问题的方法和链接到搭建好的环境。
mybatis源码研究、搭建mybatis源码运行的环境
|
1月前
|
Web App开发 前端开发 关系型数据库
基于SpringBoot+Vue+Redis+Mybatis的商城购物系统 【系统实现+系统源码+答辩PPT】
这篇文章介绍了一个基于SpringBoot+Vue+Redis+Mybatis技术栈开发的商城购物系统,包括系统功能、页面展示、前后端项目结构和核心代码,以及如何获取系统源码和答辩PPT的方法。
|
1月前
|
供应链 前端开发 Java
服装库存管理系统 Mybatis+Layui+MVC+JSP【完整功能介绍+实现详情+源码】
该博客文章介绍了一个使用Mybatis、Layui、MVC和JSP技术栈开发的服装库存管理系统,包括注册登录、权限管理、用户和货号管理、库存管理等功能,并提供了源码下载链接。
服装库存管理系统 Mybatis+Layui+MVC+JSP【完整功能介绍+实现详情+源码】
|
2月前
|
SQL 缓存 Java
【面试官】Mybatis缓存有什么问题吗?
面试官:你说下对MyBatis的理解?面试官:那SqlSession知道吧?面试官:Mybatis的缓存有哪几种?面试官:那Mybatis缓存有什么问题吗?面试官:Mybatis分页插件是怎么
【面试官】Mybatis缓存有什么问题吗?
|
1月前
|
缓存 Java 数据库连接
我要手撕mybatis源码
该文章深入分析了MyBatis框架的初始化和数据读写阶段的源码,详细阐述了MyBatis如何通过配置文件解析、建立数据库连接、映射接口绑定、动态代理、查询缓存和结果集处理等步骤实现ORM功能,以及与传统JDBC编程相比的优势。
我要手撕mybatis源码
|
2月前
|
缓存 算法 Java
关于MyBatis的缓存详解
MyBatis 的缓存机制非常灵活,可以通过简单的配置来满足不同的性能需求。合理地使用缓存可以显著提高应用程序的性能,尤其是在处理大量数据库查询时。然而,开发者需要注意缓存的一致性和并发问题,特别是在使用可读写缓存时。
|
3月前
|
缓存 NoSQL Java
在 SSM 架构(Spring + SpringMVC + MyBatis)中,可以通过 Spring 的注解式缓存来实现 Redis 缓存功能
【6月更文挑战第18天】在SSM(Spring+SpringMVC+MyBatis)中集成Redis缓存,涉及以下步骤:添加Spring Boot的`spring-boot-starter-data-redis`依赖;配置Redis连接池(如JedisPoolConfig)和连接工厂;在Service层使用`@Cacheable`注解标记缓存方法,指定缓存名和键生成策略;最后,在主配置类启用缓存注解。通过这些步骤,可以利用Spring的注解实现Redis缓存。
67 2
|
3月前
|
SQL 缓存 Java
Java框架之MyBatis 07-动态SQL-缓存机制-逆向工程-分页插件
Java框架之MyBatis 07-动态SQL-缓存机制-逆向工程-分页插件