小撕Mybatis(一)

236 阅读3分钟

引言

Mybtais是一种ORM框架,用于实现面向对象编程语言里不同类型系统的数据之间的转换。

本文我将从三个步骤进行Mybatis解读。

Mybatis 如何获取源

搭建Mybatis首先肯定要有配置文件mybatis-config.xml啦,但以下的配置文件只是独立使用Mybatis,并没有在Spring框架中集成MyBatis。

<?xml version="1.0" encoding="UTF-8" ?> 
  <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
  "http://mybatis.org/dtd/mybatis-3-config.dtd"> 
  <configuration> 
     <properties resource="jdbc.properties"/>
  <environments default="development"> 
  <environment id="development"> 
  <transactionManager type="JDBC"/> 
  <dataSource type="POOLED"> 
  <property name="driver" value="${driver}"/> 
  <property name="url" value="${url}"/> 
  <property name="username" value="${username}"/> 
  <property name="password" value="${password}"/> 
  </dataSource> 
  </environment> 
  </environments>
  <mappers> 
  <mapper resource="org/mybatis/example/BlogMapper.xml"/> 
  </mappers> 
  </configuration>

测试代码

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = SqlSessionFactory.openSession();
Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog");
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
  try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
  	......
  }
}

在这时XMLConfigBuilder会用来解析xml并构建

进入parse();

public Configuration parse() {
    //如果已经解析过了,报错
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
  
    //根节点是configuration
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

如果此配置文件已经解析过了,就报错。

此时parser.evalNode("/configuration")是用来获取配置文件的根节点

  <configuration> 
     <properties resource="jdbc.properties"/>
  <environments default="development"> 
  <environment id="development"> 
  <transactionManager type="JDBC"/> 
  <dataSource type="POOLED"> 
  <property name="driver" value="${driver}"/> 
  <property name="url" value="${url}"/> 
  <property name="username" value="${username}"/> 
  <property name="password" value="${password}"/> 
  </dataSource> 
  </environment> 
  </environments>
  <mappers> 
  <mapper resource="org/mybatis/example/BlogMapper.xml"/> 
  </mappers> 
  </configuration>
  //解析配置
  private void parseConfiguration(XNode root) {
    try {
      //分步骤解析
      //issue #117 read properties first
      //1.properties
      propertiesElement(root.evalNode("properties"));
      //2.类型别名
      typeAliasesElement(root.evalNode("typeAliases"));
      //3.插件
      pluginElement(root.evalNode("plugins"));
      //4.对象工厂
      objectFactoryElement(root.evalNode("objectFactory"));
      //5.对象包装工厂
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //6.设置
      settingsElement(root.evalNode("settings"));
      // read it after objectFactory and objectWrapperFactory issue #631
      //7.环境
      environmentsElement(root.evalNode("environments"));
      //8.databaseIdProvider
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //9.类型处理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      //10.映射器
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

通过evalNode()来解析标签

接下来进入environmentsElement(),可以查看context。

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
		//循环比较id是否就是指定的environment
        if (isSpecifiedEnvironment(id)) {
          //7.1事务管理器
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //7.2数据源
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

这时候会发现context已经讲数据源的连接取出来了。

<environments default="development">
	  <environment id="development">
	    <transactionManager type="JDBC">
	      <property name="..." value="..."/>
	    </transactionManager>
	    <dataSource type="POOLED">
	      <property name="driver" value="com.mysql.jdbc.Dirver"/>
	      <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
	      <property name="username" value="root"/>
	      <property name="password" value="123456"/>
	    </dataSource>
	  </environment>
	</environments>

但这时有一个问题,driver、url、username、password的值什么时候变化了呢?

 <properties resource="jdbc.properties"/>

答案就在这,里面记录了driver、url、username、password之类的值,那这时候又有人要问了,能放在 下面吗,其实这时候你可以点击下面这个链接。

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
  "http://mybatis.org/dtd/mybatis-3-config.dtd"> 

里面详细介绍了每个标签各自的属性、在它下面包括哪些标签,贴图为证。

<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>

<!ELEMENT databaseIdProvider (property*)>
<!ATTLIST databaseIdProvider
type CDATA #REQUIRED
>

<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>

之后在刚才的代码中有这样一句

DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();

DataSource会取出driver、url、username、password,如下图。

接下来有一句非常关键的代码

configuration.setEnvironment(environmentBuilder.build());

这时候就引出Environment类,大家看代码应该就能明白了。

public final class Environment {
  //环境id
  private final String id;
  //事务工厂
  private final TransactionFactory transactionFactory;
  //数据源
  private final DataSource dataSource;
  ....
  ..
  .
  }

就是这个类将XML中的数据源存储进Mybatisy以及决定加载哪种环境(开发环境/生产环境)。

你可以发现数据源中的标签在Environment都一一对应。!!! 划重点

一篇放不下传送门