How Mybatis works

Keywords: Java Mybatis MySQL xml JDBC

Objectives of this paper:

  1. Using the pure Mybatis framework to obtain data;
  2. Clarify the working process of Mybatis.

Create project and run

First, create maven project, and the process will not be repeated. The dependence is as follows:

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

Here is a table:

CREATE TABLE `clips` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary key',
  `content` varchar(256) NOT NULL DEFAULT '' COMMENT 'content',
  `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Delete identity: 0 normal, 1 delete',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update time',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='clips';

Add a piece of data:

Corresponding entity class:

public class ClipsEntity {
    private Integer id;
    private String content;
    private Integer deleted;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    
    // Omit getter and setter
}

DAO:

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

mapper file:

<?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 profile:

<?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>
    <!--Use jdbc Of getGeneratekeys Get auto increment primary key value-->
    <setting name="useGeneratedKeys" value="true"/>
    <!--Replace alias with column alias  default true-->
    <setting name="useColumnLabel" value="true"/>
    <!--Turn on hump naming conversion Table:create_time reach Entity(createTime)-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!-- Print query statement -->
    <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>

Here's a test:

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

Operation result:

Run successfully.
So, in this process, what does the program do? Step by step.
First, we generate an InputStream with a configuration file, then generate a SqlSessionFactory with InputStream, then get the Session, get the corresponding mapper, and execute SQL to get the result. There are three main steps for Mybatis to do:

  1. Generate SqlSessionFactory from the configuration file;
  2. Get the session from SqlSessionFactory;
  3. Get the corresponding mapper and execute SQL.

Let's look at the source code step by step.

Load mybatis configuration to generate SqlSessionFactory

  // This method is called first:
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
  // And then this:
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
    // Get an XMLConfigBuilder according to the parameters, which is the key point
      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.
      }
    }
  }
  
  // The returned build method is as follows. You can see that the implementation is DefaultSqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

The annotation part is very clear. Next, focus on XMLConfigBuilder. Mybatis uses this class to parse the corresponding configuration of mybatis.

  // Resolves the child nodes under the configuration node and returns the final 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
      // Load the properties under the properties node,
      propertiesElement(root.evalNode("properties"));
      // Load properties under settings node
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      // Load alias configuration
      typeAliasesElement(root.evalNode("typeAliases"));
      // Load plug-in configuration
      pluginElement(root.evalNode("plugins"));
      // Load objectFactory configuration
      objectFactoryElement(root.evalNode("objectFactory"));
      // Load objectWrapperFactory configuration
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      // Load reflectorFactory configuration
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      // Load environment configuration, where transaction manager will be configured
      environmentsElement(root.evalNode("environments"));
      // Load databaseIdProvider configuration
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      // Loading typeHandler is a configuration. The custom typeHandler will be registered here
      typeHandlerElement(root.evalNode("typeHandlers"));
      // Load mapper configuration
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
  

At this point, mybatis configuration loading is complete.

Summary

This article mainly introduces how to use pure Mybatis to operate the database, and then introduces the process of Mybatis loading configuration. The content is relatively shallow, and the in-depth analysis is as follows.

Posted by samsolomonraj on Sat, 07 Dec 2019 18:48:51 -0800