mybatis源码-sqlSesionFactory

839 阅读8分钟

使用xml形式配置 sqlSesionFactory

<!-- myBatis文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        //设置数据源
	<property name="dataSource" ref="dataSource" />
	 //mybatis 的config
	<property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
	//要处理的 sql 的xml,上篇文说过
	<property name="mapperLocations" value="classpath*:mappers/*/*Mapper.xml" />
</bean>
//springboot 项目中是spingboot帮我们注册的我们无需自己配置

//是在 mybatisAutoConfiguration 中创建的
@Bean
@ConditionalOnMissingBean
// 这个 dataSource 是在 dataSourceConfiguraation 中 @bean 创建的 这里就不介绍了
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    //就是 SqlSessionFactoryBean 创建了 SqlSessionFactory 
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    //下面 给 factoryBean 设置属性
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    Configuration configuration = this.properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
      configuration = new Configuration();
    }
    if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
      for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
        customizer.customize(configuration);
      }
    }
    factory.setConfiguration(configuration);
    if (this.properties.getConfigurationProperties() != null) {
      factory.setConfigurationProperties(this.properties.getConfigurationProperties());
    }
    if (!ObjectUtils.isEmpty(this.interceptors)) {
      factory.setPlugins(this.interceptors);
    }
    if (this.databaseIdProvider != null) {
      factory.setDatabaseIdProvider(this.databaseIdProvider);
    }
    if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
      factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
    }
    if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
      factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
    }
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
        //这些设置属性的 就这个比较的重要,获取到了我们的指定的所有的mapper文件
        //这个地方有个 properties 这个是 MybatisProperties 
        //MybatisProperties类上面有个@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX),意思就是
        //把配置的参数前缀为MybatisProperties.MYBATIS_PREFIX 绑定到 MybatisProperties类,这个自动绑定有时间单独讲
        //这里只要知道获取到了 所有的 mapper文件的路径就行了
      factory.setMapperLocations(this.properties.resolveMapperLocations());
    }
    //这里 .  重点  
    return factory.getObject();
}


 @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
        //这里
      afterPropertiesSet();
    }
    //最后返回的是 sqlSeeionFactoryBean 的  sqlSessionFactory 属性
    return this.sqlSessionFactory;
  }
 @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");
    
    //上面说了 返回的是 sqlSessionFactory  这里就赋值了,所以主要看看 buildSqlSessionFactory(); 干了什么
    this.sqlSessionFactory = buildSqlSessionFactory();
  }
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;

    XMLConfigBuilder xmlConfigBuilder = null;
    
..........................................................................................................
    if (!isEmpty(this.mapperLocations)) {
    //这个地方循环的是我们的 mapper文件
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        try {
        //创建 xmlMapperBuilder 看下这个方法
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              configuration, mapperLocation.toString(), configuration.getSqlFragments());
        // 去解析     
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }

        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
      }
    }
    //把 configuration 当做参数 构造 sqlSessionFactory
    return this.sqlSessionFactoryBuilder.build(configuration);
  }
  
  
  //
  public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    //主要就是干了这么一件事 
    this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
        configuration, resource, sqlFragments);
  }
  
  public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    //创建了 document  点进去会发现使用的  是  w3c 的 dom
    this.document = createDocument(new InputSource(inputStream));
  }

看下相关的结构

xmlMapperBuilder
		configuration
		XPathParser
			document
			XPath
		MapperBuilderAssistant
			configuration
			resource
public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
        //最重要的就是这行代码的两个方法
        //先看evalNode 在看 configurationElement
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }
    
    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}
public XNode evalNode(String expression) {
    return evalNode(document, expression);
}
//获取 mapper 标签 的 document
public XNode evalNode(Object root, String expression) {
    //看下这个方法 获取node
    Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
    if (node == null) {
      return null;
    }
    //封装为 XNode
    return new XNode(this, node, variables);
}
private void configurationElement(XNode context) {
    try {
        //获取 名字属性名为 namespace  也就是我们 mapper 文件的命名空间
      String namespace = context.getStringAttribute("namespace");
      //命名空间不能为空,否则报错了
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //给MapperBuilderAssistant属性 传递命名空间的参数
      builderAssistant.setCurrentNamespace(namespace);
      //是否存在缓存,这里多说点,我做过的项目都没使用过这个缓存
      cacheRefElement(context.evalNode("cache-ref"));
      //是否引用缓存
      cacheElement(context.evalNode("cache"));
      //这个基本被废弃了,官网上也是不推荐使用了
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //映射结果集的标签 我们常用的
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //获取 sql 标签,也是经常使用的
      sqlElement(context.evalNodes("/mapper/sql"));
      //context.evalNodes 获取 增删改查的标签,,主要看下buildStatementFromContext方法
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }
private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    //这里
    buildStatementFromContext(list, null);
}

 private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    //循环着去处理每个sql语句
    for (XNode context : list) {
        //创建构造器,注意下传递的参数
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
      //具体操作
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

//这里面的代码有点多
public void parseStatementNode() {
    //获取sql 的id
    String id = context.getStringAttribute("id");
    //数据库厂商标识 一般是用不到的
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
    //这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等  (没啥用)
    Integer fetchSize = context.getIntAttribute("fetchSize");
    // 这个sql 执行的超时时间
    Integer timeout = context.getIntAttribute("timeout");
    //当前sql 的 parameterMap 基本被废弃了
    String parameterMap = context.getStringAttribute("parameterMap");
    //常用的请求参数类型
    String parameterType = context.getStringAttribute("parameterType");
    //转化为 class 类型
    Class<?> parameterTypeClass = resolveClass(parameterType);
    //返回值 map 都是常用的
    String resultMap = context.getStringAttribute("resultMap");
    //resultType
    String resultType = context.getStringAttribute("resultType");
    //这个我不知道是干啥的
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);
    // 转 class
    Class<?> resultTypeClass = resolveClass(resultType);
    //不常用的属性
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    //获取标签名称  select insert  update delete 
    String nodeName = context.getNode().getNodeName();
    //转大写
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    //判断是否为 select 语句
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    //下面两行代码为处理 二级缓存的
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    //处理 include
    includeParser.applyIncludes(context.getNode());

    // Parse selectKey after includes and remove them.
    //处理 selectKey  说实话 这个标签没咋用过
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    //这个是我们的重点了,获取sql 资源  , 先看下这个方法
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
   .............................下面的代码先省略,先看下上面这行代码的流程.......................................
  }

@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    //创建 XMLScriptBuilder , 除了创建一个实例出来,最重要是的是添加 不同 标签的 处理器,放在map中
    //例如  <where> 的 nodeHandlerMap.put("where", new WhereHandler());
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    //去解析
    return builder.parseScriptNode();
}

public SqlSource parseScriptNode() {
    //这里 ,  这里的话我们先看下 SqlNode 的继承结构 , 然后看下 parseDynamicTags 方法
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource = null;
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
        //封装为 sqlSource
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
}
// 看到下面的实现了,应该明白每个实现类 很对的是不同的标签
sqlNode
	方法 
	    boolean apply(DynamicContext context);
	实现类
		MixedSqlNode
			属性
			List<SqlNode> contents
		ChooseSqlNode
		IfSqlNode
		ForEachSqlNode
		StaticTextSqlNode
		TextSqlNode
		TrimSqlNode
			SetSqlNode
			WhereSqlNode
		VarDeclSqlNode
//具体的解析slq , 这里先属性下, 在 sqlSeeisonFactory 初始化的过程中 虽然解析了sql,但是并没有拼接sql
//解析sql 只是把 每个标签 解析为对应的 sqlNode 存放到 SqlSource
//说明下: 这个方法是会被递归调用的,也不是递归调用,就是每个 动态sql的解析 都会多次调用这个方法
protected MixedSqlNode parseDynamicTags(XNode node) {
    //首先创建一个 list 存放 sqlNode  这个contents 会一直传递下去
    List<SqlNode> contents = new ArrayList<SqlNode>();
    //获取node 的所有自孩子,就假如 现在传进来的是 <select> 
    //那么它的孩子 会出现 textSqlnode,或许还有 WhereSqlNode IfSqlNode,ChooseSqlNode.... 
    //假如是 <where> 孩子中会有 textSqlnode 获取还会有 IfSqlNode,ChooseSqlNode.... 
    NodeList children = node.getNode().getChildNodes();
    //循环这些 ChildNode
    for (int i = 0; i < children.getLength(); i++) {
      //获取sqlNode
      XNode child = node.newXNode(children.item(i));
      //这个地方判断这个当前的sqlNode 是不是 textSqlNode 类型的....
      //为啥要做这个判断呢,因为 假如是 textSqlNode 那么就可以直接保存了,假如不是说明还是个标签,需要继续解析
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        //获取当前sqlNode的文本
        String data = child.getStringBody("");
        //创建一个 textSqlNode
        TextSqlNode textSqlNode = new TextSqlNode(data);
        //判断是否动态的
        if (textSqlNode.isDynamic()) {
            //添加到 contents
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
            //不是动态的添加 StaticTextSqlNode
          contents.add(new StaticTextSqlNode(data));
        }
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        //获取标签名称
        String nodeName = child.getNode().getNodeName();
        //根据名称,获取对应的处理器
        NodeHandler handler = nodeHandlerMap.get(nodeName);
        
        if (handler == null) {
            //假如没有找到处理器,会报错,基本保证错 就是你的抱歉名称写错了
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        //处理 ....   我们就看一个 WhereHandler 的吧 别的不看了
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return new MixedSqlNode(contents);
}


//whereHandler 代码如下
@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
  //又调用了parseDynamicTags方法
  MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
  //封装为对应的sqlNode
  WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
  //存放到 targetContents 就是 第一次调用的parseDynamicTags 中创建的contents
  targetContents.add(where);
}

//接着看主线代码
public void parseStatementNode() {
     //获取sql 的id
    String id = context.getStringAttribute("id");
    //数据库厂商标识 一般是用不到的
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
    //这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等  (没啥用)
    Integer fetchSize = context.getIntAttribute("fetchSize");
    // 这个sql 执行的超时时间
    Integer timeout = context.getIntAttribute("timeout");
    //当前sql 的 parameterMap 基本被废弃了
    String parameterMap = context.getStringAttribute("parameterMap");
    //常用的请求参数类型
    String parameterType = context.getStringAttribute("parameterType");
    //转化为 class 类型
    Class<?> parameterTypeClass = resolveClass(parameterType);
    //返回值 map 都是常用的
    String resultMap = context.getStringAttribute("resultMap");
    //resultType
    String resultType = context.getStringAttribute("resultType");
    //这个我不知道是干啥的
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);
    // 转 class
    Class<?> resultTypeClass = resolveClass(resultType);
    //不常用的属性
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    //获取标签名称  select insert  update delete 
    String nodeName = context.getNode().getNodeName();
    //转大写
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    //判断是否为 select 语句
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    //下面两行代码为处理 二级缓存的
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    //处理 include
    includeParser.applyIncludes(context.getNode());

    // Parse selectKey after includes and remove them.
    //处理 selectKey  说实话 这个标签没咋用过
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    //获取了 sqlSource
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    //下面也是设置一些属性
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    KeyGenerator keyGenerator;
    //获取 sql id  大概张这个样  XXX!selectKey
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    //前面在 拼接上命名空间 属性值
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }
    //把上面获取的 值 封装 
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
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
    id = applyCurrentNamespace(id, false);
    //判断是不是  select 语句
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    //创建 statementBuilder
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);
    //获取 statementParameterMap
    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }
    获取 statement
    MappedStatement statement = statementBuilder.build();
    //存放在 configuration 的 mappedStatements 属性中
    configuration.addMappedStatement(statement);
    //结束
    return statement;
}

总结

1. 创建sqlSessionFactory 是在 sqlSessionFactoryBean 创建的
2. 只要干了两件事情,设置数据源,解析mapper文件
3. 解析出来的sql 是放在 configuration中的, configuration 是sqlSessionFactory的一个属性