mybais启动 - 969251639/study GitHub Wiki

最简单的一个mybatis的用法如下

	public static void main(String[] args) throws IOException {
			String resource = "mybatis-config.xml";
  //读取mybatis配置
	        InputStream inputStream = Resources.getResourceAsStream(resource);
	        //从 XML 中构建 SqlSessionFactory
	        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//打开会话
	        SqlSession session = sqlSessionFactory.openSession();
	        try {
  //通过会话操作sql
	        	List<Blog> blogs = session.selectList("test.Blog.findById", 67);
	        	System.out.println(blogs);
	        	blogs = session.selectList("test.Blog.findById", 2);
	        	System.out.println(blogs);
                //提交事务
	        	session.commit();
	        } finally {
     //关闭会话
	            session.close();
	        }
	}

可以看到mybatis的启动时需要读取mybatis的一个xml配置,一个简单mybatis配置如下:

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

    <!-- 配置环境,制定数据库连接信息 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>

    <mappers>
      <mapper resource="blog.xml"/>
   </mappers>
</configuration>

其中environments节点可以定义多个子节点,用于多环境切换,default表示默认环境
environment下面有一个dataSource,type可以指定如下几种类型

  • JNDI
  • POOLED
  • UNPOOLED

这个xml的载入点在SqlSessionFactoryBuilder类的build方法中

  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

上面最重要的一个方法是XMLConfigBuilder.parse,里面会真正进行对配置的解析处理

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    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);
    }
  }

可以看到处理的根结点就是configuration,下面简单分析下environments的解析

  private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {//environment为空则使用default的配置
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {//逐个解析下面的environment节点
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {//根据environment找到对应environment节点,也就是找到对应的环境配置项
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//解析transactionManager类
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));//解析dataSource类
          DataSource dataSource = dsFactory.getDataSource();//获取数据源
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)//设置事物处理类
              .dataSource(dataSource);//设置数据源
          configuration.setEnvironment(environmentBuilder.build());//完成环境配置
        }
      }
    }
  }

  private boolean isSpecifiedEnvironment(String id) {
    if (environment == null) {
      throw new BuilderException("No environment specified.");
    } else if (id == null) {
      throw new BuilderException("Environment requires an id attribute.");
    } else if (environment.equals(id)) {
      return true;
    }
    return false;
  }

  private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
  }

  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

在transactionManagerElement方法和dataSourceElement方法中都可以根据type属性找到相应的实现类,如上面的一个xml配置

<environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
 </environment>

transactionManager的type是JDBC
dataSource的type是POOLED
两者共同调用resolveClass方法进行实例化

  protected Class<?> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
  }
  protected Class<?> resolveAlias(String alias) {
    return typeAliasRegistry.resolveAlias(alias);
  }

其中typeAliasRegistry是BaseBuilder类中初始化

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }

所以这里的typeAliasRegistry也就是Configuration类中的typeAliasRegistry
在Configuration的构造方法中会初始化typeAliasRegistry各种映射关系

public class ResultMap {
...
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();

public Configuration(Environment environment) {
    this();
    this.environment = environment;
  }

  public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }
...
}

所以上面transactionManager的type所对应的实现类是JdbcTransactionFactory,而dataSource的type所对应的实现类是PooledDataSourceFactory,其他的节点解析也都是很类似的。最后解析完得到一个SqlSessionFactory。

如果想扩展mybatis的话也可以通过typeAliasRegistry.registerAlias注册自己的别名和实现类的映射,比如创建自己的数据源或者第三方的数据源(c3p0,druid等),然后在datasource的type属性中填写自己的别名即可,当然还有其他方式扩展,比如通过属性方式注入

<dataSource type="xxx">

加载完配置文件后就可以通过创建会话进行各种sql操作了

⚠️ **GitHub.com Fallback** ⚠️