Mybatis架构原理--二级缓存源码剖析

本文深入剖析Mybatis的二级缓存机制,详细解释了二级缓存的工作流程,包括其与一级缓存的关系,如何启用及配置,以及在查询时的执行顺序。在二级缓存未命中时,查询会逐级下探至一级缓存和数据库。通过分析``标签、CachingExecutor和TransactionalCache,揭示了数据存储和读取的逻辑,强调了事务提交在缓存更新中的关键作用。
摘要由CSDN通过智能技术生成

Mybatis架构原理--二级缓存源码剖析

二级缓存构建在一级缓存之上,在收到查询请求时,Mybatis首先会查询二级缓存,若二级缓存没有命中,再去查询一级缓存,一级缓存没有,在查询数据库;

二级缓存-->一级缓存-->数据库

与一级缓存不同,二级缓存和具体命名空间绑定,一个mapper中有一个cache,相同mapper中的mappedStatement共用一个Cache,一级缓存则是和sqlSession绑定;

启用二级缓存

  • 开启全局二级缓存配置:

    <settings>
        <setting name = "cacheEnabled" value = "true"/>
    </settings>
  • 在需要使用二级缓存的Mapper配置文件中配置 <cache> 标签

    <cache></cache>
  • 在具体CRUD标签上配置useCache = true

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

标签 <cache/> 解析

//调用的重载方法
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 创建 XMLConfigBuilder, XMLConfigBuilder是专门解析mybatis的配置文件的类
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // 执行 XML 解析
            // 创建 DefaultSqlSessionFactory 对象
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }
​
--> parse()
    //解析 XML 成 Configuration 对象。
    public Configuration parse() {
        // 若已解析,抛出 BuilderException 异常
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        // 标记已解析
        parsed = true;
        ///parser是XPathParser解析器对象,读取节点内数据,<configuration>是MyBatis配置文件中的顶层标签
        // 解析 XML configuration 节点
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
​
--> parseConfiguration()
    //具体 MyBatis 有哪些 XML 标签,参见 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
    private void parseConfiguration(XNode root) {
        try {
            //issue #117 read properties first
            // 解析 <properties /> 标签
            propertiesElement(root.evalNode("properties"));
            // 解析 <settings /> 标签
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            // 加载自定义的 VFS 实现类
            loadCustomVfs(settings);
            // 解析 <typeAliases /> 标签
            typeAliasesElement(root.evalNode("typeAliases"));
            // 解析 <plugins /> 标签
            pluginElement(root.evalNode("plugins"));
            // 解析 <objectFactory /> 标签
            objectFactoryElement(root.evalNode("objectFactory"));
            // 解析 <objectWrapperFactory /> 标签
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            // 解析 <reflectorFactory /> 标签
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            // 赋值 <settings /> 到 Configuration 属性
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
            // 解析 <environments /> 标签
            environmentsElement(root.evalNode("environments"));
            // 解析 <databaseIdProvider /> 标签
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            // 解析 <typeHandlers /> 标签
            typeHandlerElement(root.evalNode("typeHandlers"));
            // 解析 <mappers /> 标签
            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()) {
                // 如果是 package 标签,则扫描该包
                if ("package".equals(child.getName())) {
                    // 获取 <package> 节点中的 name 属性
                    String mapperPackage = child.getStringAttribute("name");
                    // 从指定包中查找 mapper 接口,并根据 mapper 接口解析映射配置
                    configuration.addMappers(mapperPackage);
                // 如果是 mapper 标签,
                } else {
                    // 获得 resource、url、class 属性
                    String resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    String mapperClass = child.getStringAttribute("class");
​
                    // resource 不为空,且其他两者为空,则从指定路径中加载配置
                    if (resource != null && url == null && mapperClass == null) {
                        ErrorContext.instance().resource(resource);
                        // 获得 resource 的 InputStream 对象
                        InputStream inputStream = Resources.getResourceAsStream(resource);
                        // 创建 XMLMapperBuilder 对象
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                        // 执行解析
                        mapperParser.parse();
                        // url 不为空,且其他两者为空,则通过 url 加载配置
                    } else if (resource == null && url != null && mapperClass == null) {
                        ErrorContext.instance().resource(url);
     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值