mybatis 源码刨析扩展之 二级缓存

本文详细剖析了MyBatis二级缓存的启动、配置解析、查询时的源码流程以及事务相关缓存处理。通过源码解读,揭示了二级缓存在MyBatis中的工作原理,包括如何构建Cache对象、如何处理查询语句以及事务对缓存的影响。文章还探讨了更新操作时二级缓存的刷新机制,并总结了二级缓存的设计特点和适用场景。
摘要由CSDN通过智能技术生成

一、复习一下二级缓存内容

二级缓存构建在一级缓存之上,在收到查询请求时,MyBatis 首先会查询二级缓存,若二级缓存未命
中,再去查询一级缓存,一级缓存没有,再查询数据库。
二级缓存 -----> 一级缓存 -----> 数据库。
与一级缓存不同,二级缓存和具体的命名空间绑定,一个 Mapper 中有一个 Cache,相同 Mapper 中的 MappedStatement 共用一个 Cache,一级缓存则是和 SqlSession 绑定。

如果要继续了解详情请看 mybatis 缓存

1、启动二级缓存

分为三步走:

1)开启全局二级缓存配置

<settings>
   <setting name="cacheEnabled" value="true"/>
</settings>

2)在需要使用二级缓存的 Mapper 配置文件中配置标签

<cache></cache>

3)在具体 CURD 标签上配置 useCache=true

<select id="findById" resultType="com.demo.pojo.User" useCache="true">
    select * from user where id = #{id}
</select>

二、源码刨析

1、配置初始化,标签 的解析

根据之前的 mybatis 源码剖析,xml 的解析工作主要交给 XMLConfigBuilder.parse() 方法来实现。

1)XMLConfigBuilder

// parse()
public Configuration parse() {
   
    if (parsed) {
   
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // 在这里
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

// parseConfiguration()
// 既然是在 xml 中添加的,那么我们就直接看关于 mappers 标签的解析
private void parseConfiguration(XNode root) {
   
    try {
   
        // issue #117 read properties first
        propertiesElement(root.evalNode("properties"));
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        // read it after objectFactory and objectWrapperFactory issue #631
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        // 就是这里
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
   
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " 
                + e, e);
    }
}

// mapperElement()
private void mapperElement(XNode parent) throws Exception {
   
    if (parent != null) {
   
        for (XNode child : parent.getChildren()) {
   
            if ("package".equals(child.getName())) {
   
                String mapperPackage = child.getStringAttribute("name");
                configuration.addMappers(mapperPackage);
            } else {
   
                String resource = child.getStringAttribute("resource");
                String url = child.getStringAttribute("url");
                String mapperClass = child.getStringAttribute("class");
                // 按照我们本例的配置,则直接走该 if 判断。
                if (resource != null && url == null && mapperClass == null) {
   
                    ErrorContext.instance().resource(resource);
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, 
                            configuration, resource, configuration.getSqlFragments());
                    // 生成 XMLMapperBuilder,并执行其 parse 方法。
                    mapperParser.parse();
                } else if (resource == null && url != null && mapperClass == null) {
   
                    ErrorContext.instance().resource(url);
                    InputStream inputStream = Resources.getUrlAsStream(url);
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, 
                            configuration, url, configuration.getSqlFragments());
                    mapperParser.parse();
                } else if (resource == null && url == null && mapperClass != null) {
   
                    Class<?> mapperInterface = Resources.classForName(mapperClass);
                    configuration.addMapper(mapperInterface);
                } else {
   
                    throw new BuilderException("A mapper element may only specify a url,"
                            + " resource or class, but not more than one.");
                }
            }
        }
    }
}

2)我们来看看解析 Mapper.xml

XMLMapperBuilder

// parse()
public void parse() {
   
    if (!configuration.isResourceLoaded(resource)) {
   
        configurationElement(parser.evalNode("/mapper"));
        configuration.addLoadedResource(resource);
        bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}

// configurationElement()
private void configurationElement(XNode context) {
   
    try 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jason559

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值