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