您当前的位置: 首页 >  sql

止步前行

暂无认证

  • 2浏览

    0关注

    247博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

MyBatis工作原理源码分析(一)——SqlSessionFactory的初始化

止步前行 发布时间:2019-02-17 14:27:47 ,浏览量:2

一、引言

在Spring项目配置中,配置Mybatis时会有如下配置,这样,Spring容器会自动将SqlSessionFactory注入到容器中。


	
	
	
	

但手动使用Mybatis时,通常有如下四个步骤:

获取SqlSessionFactory对象;
获取sqlSession对象;
获取接口的代理对象(MapperProxy);
执行增删改查sql方法;

对应的代码如下:

@Test
public void test() throws IOException{
 	   String resource = "mybatis-config.xml";
       InputStream inputStream = Resources.getResourceAsStream(resource);
       
       // 1、获取sqlSessionFactory对象
       SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
       // 2、获取sqlSession对象
       SqlSession openSession = sqlSessionFactory.openSession();
       try {
           // 3、获取接口的实现类对象,会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
           DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
           // 4、执行增删改查方法
           Department dept = mapper.getDeptById(1);
           System.out.println(dept);
       } finally {
           openSession.close();
       }
    }
二、源码分析

下面来分析第一步,SqlSessionFactory 是如何创建的?

1、首先,SqlSessionFactoryBuilder会去读取mybatis的配置文件,然后build一个DefaultSqlSessionFactory。源码如下:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {

    try {
      //创建XMLConfigBuilder解析器,用于解析mybatis的配置文件
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      
      //第一步:调用解析器的parse()方法进行mybatis配置文件的解析,返回一个Configuration!!!!
      //第二步:再将Configuration参数用build()方法创建一个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) {
      }
    }
  }
 
//用解析完成后返回的Configuration,创建一个DefaultSqlSessionFactory对象
public SqlSessionFactory build(Configuration config) {
   return new DefaultSqlSessionFactory(config);
 }

2、XMLConfigBuilder解析器的parse()解析方法,返回Configuration

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    
    //parser.evalNode(“/configuration”) 用XPathParser类的evalNode方法,获取配置文件的根节点
    //返回的是XNode对象,XNode对象保存了解析后的对应节点下的所有信息。
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

3、解析每一个标签,并把详细信息保存在Configuration对象中

private void parseConfiguration(XNode root) {
    try {

      // 解析properties节点并设置到Configuration
      propertiesElement(root.evalNode("properties"));
      
      //获取了下的节点
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      
      //解析typeAliases节点并设置到Configuration
      typeAliasesElement(root.evalNode("typeAliases"));
      //解析plugin节点并设置到Configuration
      pluginElement(root.evalNode("plugins"));
      //解析objectFactory节点并设置到Configuration
      objectFactoryElement(root.evalNode("objectFactory"));
      //解析objectWrapperFactory节点并设置到Configuration 
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      //解析settings节点并设置到Configuration  
      settingsElement(settings);
      //解析environments节点并设置到Configuration  即Environments对象
      environmentsElement(root.evalNode("environments"));
      //解析databaseIdProvider节点并设置到Configuration
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //解析typeHandlers节点并设置到Configuration    
      typeHandlerElement(root.evalNode("typeHandlers"));
      //解析mappers节点并设置到Configuration
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

4、解析配置文件中节点(如下)下的mappers节点并设置到Configuration


	

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 {
       	  //获取mapper标签resource属性
          String resource = child.getStringAttribute("resource");
          //获取mapper标签url属性
          String url = child.getStringAttribute("url");
          //获取mapper标签class属性
          String mapperClass = child.getStringAttribute("class");
          
          if (resource != null && url == null && mapperClass == null) {
          
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //用XMLMapperBuilder 解析器,解析mapper文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            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.");
          }
        }
      }
    }
  }

//XMLMapperBuilder解析器中的parse()方法用来解析mapper文件
public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      //解析mapper.xml文件中的内容
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

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

//解析mapper.xml文件中的内容,主要是增删改查标签的解析
private void configurationElement(XNode context) {
    try {
      //mapper文件中的命名空间属性
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      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"));
      
      //解析mapper.xml文件中的增删改查标签!!!!!!
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

  //解析mapper.xml文件中的增删改查标签
  private void buildStatementFromContext(List list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

private void buildStatementFromContext(List 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);
    }
  }
}

public void parseStatementNode() {
	//拿到每一个增删改查的id值
   String id = context.getStringAttribute("id");
   String databaseId = context.getStringAttribute("databaseId");

   if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
     return;
   }

   //标签能写的所有属性,都可以拿到
   Integer fetchSize = context.getIntAttribute("fetchSize");
   Integer timeout = context.getIntAttribute("timeout");
   String parameterMap = context.getStringAttribute("parameterMap");
   String parameterType = context.getStringAttribute("parameterType");
   Class parameterTypeClass = resolveClass(parameterType);
   String resultMap = context.getStringAttribute("resultMap");
   String resultType = context.getStringAttribute("resultType");
   String lang = context.getStringAttribute("lang");
   LanguageDriver langDriver = getLanguageDriver(lang);

   Class resultTypeClass = resolveClass(resultType);
   String resultSetType = context.getStringAttribute("resultSetType");
   StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
   ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

   String nodeName = context.getNode().getNodeName();
   SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
   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);
   includeParser.applyIncludes(context.getNode());

   // Parse selectKey after includes and remove them.
   processSelectKeyNodes(id, parameterTypeClass, langDriver);
   
   // Parse the SQL (pre:  and  were parsed and removed)
   SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
   String resultSets = context.getStringAttribute("resultSets");
   String keyProperty = context.getStringAttribute("keyProperty");
   String keyColumn = context.getStringAttribute("keyColumn");
   KeyGenerator keyGenerator;
   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;
   }

	//将标签中的所有信息拿出来,调用addMappedStatement()方法,该方法会返回一个MappedStatement对象。
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

此处可以知道,一个MappedStatement对象,就代表一个增删改查标签的详细信息。然后将MappedStatement存放到Configuration中

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

  ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
  if (statementParameterMap != null) {
    statementBuilder.parameterMap(statementParameterMap);
  }

  MappedStatement statement = statementBuilder.build();
  //将MappedStatement存放到Configuration中
  configuration.addMappedStatement(statement);
  return statement;
}
三、小结

上面就是根据配置文件创建SQLSessionFactory的详细过程,下面是对应的时序图: 在这里插入图片描述 总结: (1)、一个MappedStatement代表一个增删改查的详细信息 (2)、把Mybatis配置文件的信息解析并保存在Configuration对象中,返回包含了Configuration的DefaultSqlSession对象; (3)、Configuration封装了所有配置文件的详细信息,如下图所示 在这里插入图片描述

在这里插入图片描述

关注
打赏
1657848381
查看更多评论
立即登录/注册

微信扫码登录

0.0388s