mybatis缓存创建过程

带着 上篇 的问题,再来看看mybatis的创建过程

1.从SqlSessionFactoryBuilder解析mybatis-config.xml开始

对文件流解析

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());//等同于return new DefaultSqlSessionFactory(config);

关键是parser.parse()里面

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

  private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));//mapper配置文件的位置
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

关键看这:mapperElement(root.evalNode("mappers")); mapperElement中看

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();

2.XMLMapperBuilder的parse方法如下:

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

    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }

进入红色部分,代码如下:

private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace.equals("")) {
          throw new BuilderException("Mapper‘s namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));//找到<cache />
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));//<select>...标签
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

2.1 处理cache标签

private void cacheElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type", "PERPETUAL");
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
      String eviction = context.getStringAttribute("eviction", "LRU");
      Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
      Long flushInterval = context.getLongAttribute("flushInterval");
      Integer size = context.getIntAttribute("size");
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      Properties props = context.getChildrenAsProperties();
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);
    }
  }

进入MapperBuilderAssistant的useNewCache,

public Cache useNewCache(Class<? extends Cache> typeClass,
      Class<? extends Cache> evictionClass,
      Long flushInterval,
      Integer size,
      boolean readWrite,
      Properties props) {
    typeClass = valueOrDefault(typeClass, PerpetualCache.class);
    evictionClass = valueOrDefault(evictionClass, LruCache.class);
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(typeClass)
        .addDecorator(evictionClass)
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .properties(props)
        .build();
    configuration.addCache(cache);
    currentCache = cache;
    return cache;
  }

创建了一个二级cache,并记录在configuration对象中,并赋值给了currentCache

2.2回到2.1,再看对 buildStatementFromContext(context.evalNodes("select|insert|update|delete"));的处理

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

对每个标签parseStatementNode(),进入XMLStatementBuilder.parseStatementNode(),最终有句

builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
  fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
  resultSetTypeEnum, flushCache, useCache, resultOrdered,
  keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);

还记得上面的MapperBuilderAssistant的useNewCache么,一个对象

public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class<?> parameterType,
      String resultMap,
      Class<?> resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) {

    if (unresolvedCacheRef) throw new IncompleteElementException("Cache-ref not yet resolved");

    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
    statementBuilder.resource(resource);
    statementBuilder.fetchSize(fetchSize);
    statementBuilder.statementType(statementType);
    statementBuilder.keyGenerator(keyGenerator);
    statementBuilder.keyProperty(keyProperty);
    statementBuilder.keyColumn(keyColumn);
    statementBuilder.databaseId(databaseId);
    statementBuilder.lang(lang);
    statementBuilder.resultOrdered(resultOrdered);
    statementBuilder.resulSets(resultSets);
    setStatementTimeout(timeout, statementBuilder);

    setStatementParameterMap(parameterMap, parameterType, statementBuilder);
    setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
    setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder); //等同于mappedStatement.cache = cache;
    MappedStatement statement = statementBuilder.build();     configuration.addMappedStatement(statement); //存于configuration对象!!!!    return statement; }

ps:二级缓存是针对namespace的

<mapper namespace="***">

若有namespace1和namespace2,namespace2有对namespace1表的操作,namespace1刷新了缓存,而这时namespace2中的缓存仍然有效就是脏数据

参考文章:

1. 深入了解MyBatis二级缓存

时间: 2024-08-02 23:08:31

mybatis缓存创建过程的相关文章

实际测试例子+源码分析的方式解剖MyBatis缓存的概念

前言: 前方高能! 本文内容有点多,通过实际测试例子+源码分析的方式解剖MyBatis缓存的概念,对这方面有兴趣的小伙伴请继续看下去~ MyBatis缓存介绍首先看一段wiki上关于MyBatis缓存的介绍: MyBatis支持声明式数据缓存(declarative data caching).当一条SQL语句被标记为"可缓存"后,首次执行它时从数据库获取的所有数据会被存储在一段高速缓存中,今后执行这条语句时就会从高速缓存中读取结果,而不是再次命中数据库.MyBatis提供了默认下基于

【转】MaBatis学习---源码分析MyBatis缓存原理

[原文]https://www.toutiao.com/i6594029178964673027/ 源码分析MyBatis缓存原理 1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然也在内部提供了相应的支持.通过在框架层面增加缓存功能,可减轻数据库的压力,同时又可以提升查询速度,可谓一举两得.MyBatis 缓存结构由一级缓存和二级缓存构成,这两级

关于 Mybatis 缓存机制,面试官都未必知道的这么详细

欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.exception.site 一.前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用MyBatis的默认缓存配置,但是MyBatis缓存机制有一些不足之处,在使用中容易引起脏数据,形成一些潜在的隐患.个人在业务开发中也处理过一些由于MyBatis缓存引发的开发问题,带着个

聊聊MyBatis缓存机制

前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用MyBatis的默认缓存配置,但是MyBatis缓存机制有一些不足之处,在使用中容易引起脏数据,形成一些潜在的隐患.个人在业务开发中也处理过一些由于MyBatis缓存引发的开发问题,带着个人的兴趣,希望从应用及源码的角度为读者梳理MyBatis缓存机制. 本次分析中涉及到的代码和数据库表均放在GitHub上,地址: mybatis-cache-demo .欢迎大家关注我的主页,给大家分享我的经验以及架构资

MyBatis的执行过程

MyBatis的执行过程: (1)读取MyBatis配置文件mybatis-config.xml (2)加载映射文件 (3)构建会话工厂SqlSessionFactory (4)创建SqlSession对象 (5)创建Executor接口的实例. MyBatis底层定义了一个Executor接口来操作数据库,可以根据SqlSession传递的参数动态生成需要执行的SQL语句,同时负责查询缓存的维护. MyBatis给Executor接口提供了多个实现类. (6)创建MappedStatement

Spring(八)核心容器 - Bean 创建过程

目录 1.整体流程 2.核心流程 2.1 解析 FactoryBean 2.2 从 createBean 开始 2.2.1 创建 Bean 实例 2.2.2 初始化 Bean 实例 - 属性注入 2.2.3 初始化 Bean 实例 - 执行初始化方法(回调钩子接口) Spring 版本 5.0.7.RELEASE 获取 Bean 的方法是 getBean,其来自 BeanFactory 继承的 AbstractAutowireCapableBeanFactory 抽象类继承的 AbstractB

浅谈MyBatis缓存

在谈论MyBatis的缓存之前,我们先说说它的延迟加载,所谓延迟加载, resultMap中的association和collection标签具有延迟加载的功能.延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息.使用关联信息时再去加载关联信息. 设置延迟加载 需要在SqlMapConfig.xml文件中,在<settings>标签中设置下延迟加载. lazyLoadingEnabled.aggressiveLazyLoading 什么是查询缓存 Mybatis的一级缓存是指SqlS

mybatis的sql 缓存,去除mybatis缓存

第二次用到mybaits,还是被同一个问题坑了几个小时,所以一定要把这个问题分享给大家.网友很多都是说了一大堆的配置,都是在mybatis.xml中配置.但是,我是没有用mybatis.xml的,我的配置都是在每个**mapper.xml中配置,无非就是一个关联实体类的路径,和一个别名. 在mybatis框架中,在SqlSession未关闭之前,在一个session里面,如果执行相同的select语句,mybatis不会重新查询数据库,而是直接返回缓存在内存中的查询结果,这个是与MyBatis的

ASP.NET Web API 控制器创建过程(二)

ASP.NET Web API 控制器创建过程(二) 前言 本来这篇随笔应该是在上周就该写出来公布的,因为身体跟不上节奏感冒发烧有心无力,这样的天气感冒发烧生不如死,也真正的体会到了什么叫病来如山倒,病去如抽丝.这两天状态才好了一点,让我理解了什么才是革命的本钱,希望大家也多保重身体. 好了,还是回归主题,对于上一篇的内容解说的仅仅是ASP.NET Web API控制器创建过程中的一个局部知识,在接着上篇内容解说的之前,我会先回想一下上篇的内容,而且在本篇里进行整合,让我们要看到的是一个整个的创