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';

添加一条数据:

Mybatis是如何工作的(一)

对应的实体类:

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);
        }
    }
}

运行结果:

Mybatis是如何工作的(一)

运行成功。
那么,在这个过程中,程序具体做了什么事呢?一步一步来看。
首先,我们用配置文件生成了一个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加载配置的过程。内容相对粗浅,深入分析在下文。

相关推荐