浅析MyBatis的工作原理

一、Mybatis是什么

      myBatis是常见的Java数据库访问层框架,相比于传统的JDBC,Mybatis可以进行更为细致的SQL优化,减少查询字段、统一的SQL管理。

二、Mybatis的原理

      当我们使用mybatis的时候,通常需要配置一个xml文件,假如叫mybatis-config.xml文件,类似下面这样

<?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>
  <settings>
    <setting name="logImpl" value="SLF4J"/>
    <!--<setting name="cacheEnabled" value="true" />-->
  </settings>
  <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/tanjie?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="tanjie666"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="mapper/UserMapper.xml"/>
  </mappers>
</configuration>

      当我们配置了xml文件,然后需要一个mapper文件,这里我们叫UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tanjie.entity.mapper.UserMapper">
  <select id="findUserById" resultType="com.tanjie.entity.User">
      select * from  user where id = #{id}
  </select>
</mapper>

  然而mybatis是如何根据我们的配置来和DB进行数据交互的?下面我们一步一步跟着源码来解答这些疑问。

2.1 构建SqlSessionFactory

package com.tanjie.mybatis;


import com.alibaba.fastjson.JSON;
import com.tanjie.entity.User;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MybatisDemo {

  public static void main(String[] args) throws IOException {
    String resource = "mybatis-config.xml";
    //读取核心配置文件并返回InputStream流对象。
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //构建configuration对象,拿到数据源 dataSource,创建SqlSessionFactory工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //根据一系列属性从SqlSessionFactory工厂中创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //从SqlSession中调用Executor执行数据库操作&&生成具体SQL指令
    User user = sqlSession.selectOne("com.tanjie.entity.mapper.UserMapper.findUserById", 1);
    System.out.println(JSON.toJSONString(user));
  }


}
  

     在这个main方法里面,首先我们读取mybatis-config.xml文件获取了一个InputStream,下一步我们构建了一个SqlSessionFactory对象,我们就从这一步入口,先看一下,mybatis是如何构建SqlSessionFactory的

 当执行

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

会走到下面这段代码,

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      var5 = this.build(parser.parse());
    } catch (Exception var14) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
      ErrorContext.instance().reset();

      try {
        inputStream.close();
      } catch (IOException var13) {
      }

    }

    return var5;
  }

 这里面我们需要关注的是var5=this.build(parser.parse());

  public Configuration parse() {
    if (this.parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    } else {
      this.parsed = true;
      this.parseConfiguration(this.parser.evalNode("/configuration"));
      return this.configuration;
    }
  }

      上面代码片段的 核心就是解析我们的mybatis-config.xml文件,然后映射为一个Configuration对象,可以看到,传入的root对象就是我们的配置文件

  在这个方法里面,会依次解析xml,读取里面的配置,比如我们的dataSource是配置在environments节点下的,入口为

this.environmentsElement(root.evalNode("environments"));
private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (this.environment == null) {
        this.environment = context.getStringAttribute("default");
      }

      Iterator i$ = context.getChildren().iterator();

      while(i$.hasNext()) {
        XNode child = (XNode)i$.next();
        String id = child.getStringAttribute("id");
        if (this.isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
          DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
          this.configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }

  }

   再比如我们配置的mapper对应的xml文件在节点mapper下面,入口为

 this.mapperElement(root.evalNode("mappers"));

   上面的代码即是映射为一个configuration对象,最后构建一个SqlSessionFactory

 public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

   2.2 创建SqlSession

    执行代码如下

 SqlSession sqlSession = sqlSessionFactory.openSession();

 

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
      Environment environment = this.configuration.getEnvironment();
      TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      Executor executor = this.configuration.newExecutor(tx, execType);
      var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
      this.closeTransaction(tx);
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
      ErrorContext.instance().reset();
    }

    return var8;
  }

  上面的代码代码片段主要做了几件事情,

2.2.1 根据configuration的Environment属性来创建事务工厂

2.2.2 根据事务工厂创建事务

2.2.3 创建执行器Executor

2.2.4 根据执行器创建SqlSession

下面的片段可以看到SqlSession对象持有Executor

  2.3 Executor操作DB

 

  当我们执行  
User user = sqlSession.selectOne("com.tanjie.entity.mapper.UserMapper.findUserById", 1); 
  代码进入到

      根据我们传入的statement,从configuration里面拿到MappedStatement,所以mybatis是根据类名+方法名来定位一个Sql的,故我们不能在我们的mapper文件里面重载某个方法。

    然后拿到id对应的sql语句,和DB进行交互,返回Result

  

       至此,mybatis是如何和DB进行交互的流程就走完了,我们画一个简单的时序图来回顾一下

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值