跳到主要内容
  1. 所有文章/
  2. 《Mybatis3源码深度解析》笔记/

Mybatis源码-SqlSession的创建过程

·📄 1504 字·🍵 4 分钟

读取XML文件 #

MyBatis的主配置文件和Mapper配置都使用的是XML格式。MyBatis中的Configuration组件用于描述主配置文件信息,框架在启动时会解析XML配置,将配置信息转换为Configuration对象。

JDKAPI中提供了3种方式解析XML,分别为DOM、SAX和XPath。这3种方式都有各自的特点,具体优缺点读者可参考相关资料。在这3种方式中,API最易于使用的就是XPath方式,MyBatis框架中也采用XPath方式解析XML文件中的配置信息。

使用JDK提供的XPath相关API解析XML需要以下几步:

  1. 创建表示XML文档的Document对象
  2. 创建用于执行XPath表达式的XPath对象
  3. 使用XPath对象执行表达式,获取XML内容

为了简化XPath解析操作,MyBatis通过XPathParser 工具类封装了对XML的解析操作,同时使用XNode类增强了对XML节点的操作。使用XNode对象,我们可以很方便地获取节点的属性、子节点等信息。

@Test
public void testXPathParser() throws Exception {
    Reader resource = Resources.getResourceAsReader("users.xml");
    XPathParser parser = new XPathParser(resource);
    // 注册日期转换器
    DateConverter dateConverter = new DateConverter(null);
    dateConverter.setPattern("yyyy-MM-dd HH:mm:ss");
    ConvertUtils.register(dateConverter, Date.class);
    List<UserEntity> userList = new ArrayList<>();
    // 调用evalNodes()方法获取XNode列表
    List<XNode> nodes = parser.evalNodes("/users/*");
    // 对XNode对象进行遍历,获取user相关信息
    for (XNode node : nodes) {
        UserEntity userEntity = new UserEntity();
        Long id = node.getLongAttribute("id");
        BeanUtils.setProperty(userEntity, "id", id);
        List<XNode> childNods = node.getChildren();
        for (XNode childNode : childNods) {
                BeanUtils.setProperty(userEntity, childNode.getName(),
                        childNode.getStringBody());
        }
        userList.add(userEntity);
    }
    System.out.println(JSON.toJSONString(userList));
}

创建Configuration实例 #

MyBatis通过XMLConfigBuilder 类完成Configuration 对象的构建工作,关于Configuration下的子标签可以看:

属性及含义可以参考官方文档

 @Test
public void testConfiguration() throws IOException {
    Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
    // 创建XMLConfigBuilder实例
    XMLConfigBuilder builder = new XMLConfigBuilder(reader);
    // 调用XMLConfigBuilder.parse()方法,解析XML创建Configuration对象
    Configuration conf = builder.parse();
}

// 追踪代码
public Configuration parse() {
  // 防止parse()方法被同一个实例多次调用
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  // 调用XPathParser.evalNode()方法,创建表示configuration节点的XNode对象。
  // 调用parseConfiguration()方法对XNode进行处理
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

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

创建SqlSession实例 #

@Test
public void testSqlSession() throws IOException {
    // 获取Mybatis配置文件输入流
    Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
    // 通过SqlSessionFactoryBuilder创建SqlSessionFactory实例
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    // 调用SqlSessionFactory的openSession()方法,创建SqlSession实例
    SqlSession session = sqlSessionFactory.openSession();
}

// 代码追踪
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
  try {
    // 生成config对象,parser.parse()就是configuration实例的创建过程
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      reader.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}
// 根据Configuration 构造参数直接创建 SqlSessionFactory  
public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}
 

接下来研究一下sqlSessionFactory.openSession();方法

@Override
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    // 获取Mybatis主配置文件配置的环境信息
    final Environment environment = configuration.getEnvironment();
    // 创建事务管理器工厂
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    // 创建事务管理器
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    // 根据Mybatis主配置文件中指定的Executor类型创建对应的Executor实例
    final Executor executor = configuration.newExecutor(tx, execType);
    // 创建DefaultSqlSession实例
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    closeTransaction(tx); // may have fetched a connection so lets call close()
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}


public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
  this.configuration = configuration;
  this.executor = executor;
  this.dirty = false;
  this.autoCommit = autoCommit;
}