关于mybatis的执行过程的简单分析(一)

Mybatis的建构图:
(说明:最近在重新学习一下mybatis,然后因为自己也是初学者,写博客来记录自己学习的点滴,如有错误或者不恰当的地方,欢迎指出,,,也希望大佬们多多指教)
在这里插入图片描述
参考之前那篇mybatis的helloworld的博客,开始简单分析执行过程:

public class DemoTest {

    /**
     *获得sessionFactory
     * 1.getSqlSessionFactory()获得SqlSessionFactory
     * 2.sqlSessionFactory.openSession()获得session
     * 3.sqlSession.getMapper(DepartmentMapper.class)获得mapper
     * 4.mapper.getDepartmentById(1) 执行查询操作。
     *
     */
    public  SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return  sqlSessionFactory;
    }

    @Test
    public void test() throws IOException {
        //获得sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        // 获得Sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //获得mapper代理对象
        DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
        // 执行方法
        Department departmentById = mapper.getDepartmentById(1);
        // 输出结果
        System.out.println(departmentById);
    }
}

下面主要分析代码的这四个步骤:
* 1.getSqlSessionFactory()获得SqlSessionFactory
* 2.sqlSessionFactory.openSession()获得session
* 3.sqlSession.getMapper(DepartmentMapper.class)获得mapper
* 4.mapper.getDepartmentById(1) 执行查询操作。

getSqlSessionFactory()

在这里插入图片描述
简述说一下执行过程:
1.先获得mybatis-config.xml文件输入流
2.执行SqlSessionFactoryBuilder().build(inputStream)
进入SqlSessionFactoryBuilder的build方法
在这里插入图片描述
接着看bulid方法:
===》
在这里插入图片描述
解释一下第一句代码:

  XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
  根据inputStream生成一个XMLConfigBuilder 也就是xml配置的配置建造器,
  parser就是一个解释器,可以解析 xml文件

return build(parser.parse());
分析:parser.parse() 点进去看看源码,发现parser.parse()返回的是一个configuration对象
如图所示:parser.parse()
而bulider(parser.parse()) 返回的是一个DefaultSqlSessionFactory如下图所示:
这个就是我们想要获得的SqlSessionFactory 其实他是DefaultSqlSessionFactory
在这里插入图片描述
现在开始先看看parser.parse() 的执行过程
parser.parse()
1.parseConfiguration(parser.evalNode("/configuration"));
先看这个方法里面的parser.evalNode("/configuration")这里是计算configuration节点,这个configuration就是我们在mybatis-config.xml当中配置的节点。返回值是** XNode**,
2.拿到了我们配置文件当中configuration的节点信息之后,开始执行

 parseConfiguration('拿到的configuration节点')具体执行过程如下:

在这里插入图片描述
这就是根据configuration根节点,分别获得其子节点,然后对子节点的信息进行解析。
如最后一行:
看到解析mapper标签节点的这个例子mapperElement(root.evalNode("mappers"));
1.首先root.evalNode("mappers")通过根节点,获得mappers节点的信息
2.执行mapperElement(root.evalNode("mappers"))执行过程如下图:
在这里插入图片描述
2.1如果父节点不为空执行遍历操作
for (XNode child : parent.getChildren())获得的所有的子节点
if ("package".equals(child.getName()))如果获得mapper使用的是package属性例如:
配置了<package name="com.kuake.dao.mapper"></package>
那么String mapperPackage = child.getStringAttribute("name"); //获得name的值
接着 configuration.addMappers(mapperPackage)// 把mapperPackage的信息设置到configuration当中,
如果是设置了 resource 如: <mapper resource="com/kuake/dao/mapper/DepartmentMapper.xml"/>,那么执行下面的代码

          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            //获得resources的输入流
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //获得xml文件的解析器,跟之前类似
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            // 开始解析xml文件
            mapperParser.parse();
          }

** 重点看mapperParser.parse()方法**
在这里插入图片描述
第一步.configurationElement(parser.evalNode("/mapper"));//先获得mapper节点 ,然后保存节点的信息
代码如下:

  private void configurationElement(XNode context) {
    try {
    //当命名空间为null或空串的话,抛出异常
      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"));
      sqlElement(context.evalNodes("/mapper/sql"));
      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);
    }
  }

上面代码的作用:获得对应节点的属性并且进行设置。
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));为例子,走一下他代码的过程。:

  1. context.evalNodes(“select|insert|update|delete”)获得所有增删改查,节点。
  2. 执行 buildStatementFromContext(‘增删改查节点信息’)
    ** buildStatementFromContext()方法代码如下**
    在这里插入图片描述
    先进行判断configuration当中是否有getDatabaseId()没有执行,然后执行 buildStatementFromContext(list, null);
    代码如下:
  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
    //获得statementParser 解析器,遍历,,每一个节点,都会生成一个statementParser 
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
      //解析节点
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
      //statementParser添加到configuration当中
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

上述代码的主要功能如下:
1.解析每一个增删改查节点
2.new XMLStatementBuilder(…)方法获得statementParser
3.statementParser.parseStatementNode();解析节点
4.statementParser添加到configuration当中添加到configuration当中

第三点重点是的parseStatementNode()代码如下;
在这里插入图片描述
在这里插入图片描述
这个方法的作用就是,把该节点的所有信息那取出来 如:fetchSize,timeout,parameterType,resultMap…等等,然后调用appMappedStatement(),就是上图中的最后一个方法,作用是生成一个mappedStatement,保存了每一个sql节点的所有信息:
appMappedStatement()代码如下:

 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();
    configuration.addMappedStatement(statement);
    return statement;
  }

上面代码的主要几个步骤如下:
1.根据传入解析出的标签属性,创建statementBuilder 构建器
2. MappedStatement statement = statementBuilder.build(); //构建器出MappedStatement
3. configuration.addMappedStatement(statement);//将MappedStatement 保存在configuration当中。

解析mapper.xml文件小结:mapper标签信息解析,保存到全局配置文件中,其每一个增删改查方法的每一个属性都解析出来,然后调用上述的addMappedStatement(每一个属性),生成一个MappedStatement ,并且保存在configuration当中。

保存了statement之后,遍开始返回了。那么mybatis-config.xml当中的mapepr节点所引入的mapper.xml文件也就解析完成,等mybatis-config.xml这个配置文件的所有子节点都解析完成之后。所以的信息都已经保存在了configration当中,然后执行到下面这一段代码,传入了configration,就返回了我们所需要的DefaultSqlSessionFctory也就是SqlSessionFactory.
在这里插入图片描述
为了思路整理一下,加深记忆:画一下了获得sqlSessionFactory的简单时序列图
在这里插入图片描述
小结:
把所有配置文件的配置信息(mybatis-config.xml,xxxmapper.xml)保存在Configuration当中,然后调用DefaultSqlSession的构造方法,返回DefaultSqlSessionFactory,这也就是我们需要的SqlSessionFactory,其中Configuration包含了一个属性mapperStatement,就是解析每一个sql节点,所生成的,包含了sql节点的所有信息,

下面两幅图是debug时,,Configuration与mapperStatement所包含的信息。

mapperStatement:包含了每一个增删改查sql的全部配置信息。如我们所首先熟悉的:在这里插入图片描述
Configuration:包含全局配置信息,也就是mybatis-config.xml,的信息,和mapperStatement在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值