Mybatis是如何工作的(一)

本文目標:

  1. 使用純Mybatis框架獲取數據;
  2. 理清Mybatis的工作過程。

創建項目並運行

首先創建maven項目,過程不再贅述。依賴如下:

<dependencies>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.46</version>
    </dependency>
  </dependencies>

下面準備一張表:

CREATE TABLE `clips` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `content` varchar(256) NOT NULL DEFAULT '' COMMENT '內容',
  `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '刪除標識:0正常,1刪除',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='clips';

添加一條數據:

clipboard.png

對應的實體類:

public class ClipsEntity {
    private Integer id;
    private String content;
    private Integer deleted;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    
    // 省略getter和setter
}

DAO:

public interface ClipsDAO {
    ClipsEntity selectById(@Param("id") Integer id);
}

mapper文件:

<?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.chunrun.dao.ClipsDAO">

  <select id="selectById" resultType="com.chunrun.entity.ClipsEntity">
    select * from clips where id = #{id}
  </select>
</mapper>

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>
  <settings>
    <!--使用jdbc的getGeneratekeys獲取自增主鍵值-->
    <setting name="useGeneratedKeys" value="true"/>
    <!--使用列別名替換別名  默認true-->
    <setting name="useColumnLabel" value="true"/>
    <!--開啓駝峯命名轉換Table:create_time到 Entity(createTime)-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!-- 打印查詢語句 -->
    <setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl" />
  </settings>
  <typeAliases>
    <typeAlias alias="ClipsEntity" type="com.chunrun.entity.ClipsEntity"/>
  </typeAliases>
  <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/bizu"/>
        <property name="username" value="chunrun"/>
        <property name="password" value="chunrun1s"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="mapper/ClipsDAO.xml"/>
  </mappers>
</configuration>

下面寫個測試:

public class Main {

    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession session = factory.openSession();
            ClipsDAO clipsDAO = session.getMapper(ClipsDAO.class);
            ClipsEntity clipsEntity = clipsDAO.selectById(1);
            System.out.println(clipsEntity);
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

運行結果:

clipboard.png

運行成功。
那麼,在這個過程中,程序具體做了什麼事呢?一步一步來看。
首先,我們用配置文件生成了一個InputStream;然後用InputStream生成了生成SqlSessionFactory;然後獲取Session;獲取對應的mapper,執行SQL獲取結果。Mybatis做的事情主要有三步:

  1. 從配置文件中生成SqlSessionFactory;
  2. 從SqlSessionFactory中獲取session;
  3. 獲取對應的mapper,執行SQL。

下面逐步看源碼。

加載mybatis配置,生成SqlSessionFactory

  // 首先調用的是這個方法:
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
  // 然後是這個:
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
    // 根據參數獲取一個XMLConfigBuilder,這部分是重點
      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.
      }
    }
  }
  
  // 返回的build方法如下,可以看出實現是DefaultSqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

註釋部分已經很清楚了,下面重點看下XMLConfigBuilder,Mybatis通過這個類來解析mybatis對應的配置。

  // 解析configuration節點下面的子節點,並返回最終的配置。
  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
      // 加載properties節點下的屬性,
      propertiesElement(root.evalNode("properties"));
      // 加載settings節點下的屬性
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      // 加載別名配置
      typeAliasesElement(root.evalNode("typeAliases"));
      // 加載插件配置
      pluginElement(root.evalNode("plugins"));
      // 加載objectFactory配置
      objectFactoryElement(root.evalNode("objectFactory"));
      // 加載objectWrapperFactory配置
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      // 加載reflectorFactory配置
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      // 加載environment配置,這裏會配置事務管理器
      environmentsElement(root.evalNode("environments"));
      // 加載databaseIdProvider配置
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      // 加載typeHandler是配置,自定義的typeHandler會在這注冊
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 加載mapper配置
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
  

至此,mybatis配置加載完成。

小結

本文主要介紹瞭如何使用純Mybatis操作數據庫,然後介紹了Mybatis加載配置的過程。內容相對粗淺,深入分析在下文。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章