Framework - MyBatis source code step by step

Posted by dcav on Thu, 03 Feb 2022 01:17:40 +0100

brief introduction

How much do you know about MyBatis? Do you know the SqlSessionFactory construction process? Understand the XML parsing process? Do you know what Mapper agent does? This chapter takes you to read the source code with MyBatis 3.5.3, so that you can understand the essence of MyBatis instead of being limited to using it.

 

Step by step through the simple Mybatis project

Maven dependency package

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.18</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.3</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

User entity class

Directory: com \ user \ user java

@Data
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * User ID
     */
    private Long userId;
    /**
     * cell-phone number
     */
    private String mobile;
}

UserMapper} interface

Directory: com \ user \ usermapper java

public interface UserMapper {
    /**
     * query
     */
    User select(Long userId);
    /**
     * insert
     */
    int insert(User user);
    /**
     * modify
     */
    int update(User user);
    /**
     * delete
     */
    int delete(Long userId);
}

UserMapper.xml configuration file

Directory resources \ 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.user.UserMapper">
  <resultMap id="BaseResultMap" type="com.user.User">
    <id column="user_id" jdbcType="BIGINT" property="userId" />
    <result column="mobile" jdbcType="VARCHAR" property="mobile"/>
  </resultMap>
  <sql id="Base_Column_List">
    user_id, mobile
  </sql>
  <select id="select" parameterType="java.lang.Long" 
          resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from `user`
    where user_id = #{userId,jdbcType=BIGINT}
  </select>
  <delete id="delete" parameterType="java.lang.Long">
    delete from `user`
    where user_id = #{userId,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.user.User">
    insert into `user` (user_id, mobile)
    values (#{userId,jdbcType=BIGINT},
        #{mobile,jdbcType=VARCHAR})
  </insert>
  <update id="update" parameterType="com.user.User">
    update `user`
    set mobile = #{mobile,jdbcType=VARCHAR}
    where user_id = #{userId,jdbcType=BIGINT}
  </update>
</mapper>

Mybaits profile

Directory: resources \ 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>
    <properties resource="jdbc.properties"></properties>
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <environments default="test">
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

JDBC configuration file

Directory: resources \ JDBC properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://192.168.1.100:3306/mybaits
username=root
password=root

Log profile

Directory: resources \ log4j properties

log4j.rootLogger=DEBUG, stdout
log4j.logger.org.mybatis.example.BlogMapper=TRACE
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

database

CREATE TABLE `user`  (
  `user_id` bigint(19) NOT NULL COMMENT 'user ID',
  `mobile` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'cell-phone number',
  PRIMARY KEY (`user_id`) USING BTREE,
  UNIQUE INDEX `cs_user_mobile_index`(`mobile`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'user' ROW_FORMAT = Dynamic;


INSERT INTO `user` VALUES (1, '13100001001');
INSERT INTO `user` VALUES (2, '13100001002');
INSERT INTO `user` VALUES (3, '13100001003');
INSERT INTO `user` VALUES (4, '13100001004');
INSERT INTO `user` VALUES (5, '13100001005');

Test class test

public class Test {
    private static SqlSessionFactory getSessionFactory() 
            throws IOException {
        String resource = "config.xml";
        Reader reader = Resources.getResourceAsReader(resource);
        return new SqlSessionFactoryBuilder().build(reader);
    }
    public static void main(String[] args) throws IOException {
        SqlSession sqlSession = getSessionFactory().openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.select(1L);
        System.out.println(user.toString());
    }
}
Output log
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 1579132337.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5e1fa5b1]
DEBUG [main] - ==>  Preparing: select user_id, mobile from `user` where user_id = ? 
DEBUG [main] - ==> Parameters: 1(Long)
DEBUG [main] - <==      Total: 1
User(userId=1, mobile=13100001001)
Disconnected from the target VM, address: '127.0.0.1:52936', transport: 'socket'

Brief analysis

String resource = "mybaits.xml";
Reader reader = Resources.getResourceAsReader(resource);
return new SqlSessionFactoryBuilder().build(reader);

Use resources first Getresourceasreader (resource) loads the configuration xml file, and then builds SqlSessionFactory with the build method of SqlSessionFactory builder. Here, we mainly focus on the construction process of SqlSessionFactory.
1. SqlSessionFactoryBuilder delegates XMLConfigBuilder
2. XMLConfigBuilder creates an xmlmapterentityresolver DTD loader and delegates the XPath parser parser to parse mybaits XML configuration file
3. The XPathParser uses XPathFactory to load xml and DocumentBuilderFactory to build documents
4. XMLConfigBuilder uses the result of step 3 to create Configuration and assign value to Configuration, and finally completes the creation of XMLConfigBuilder
5, calling the XMLConfigBuilder parse() method in SqlSessionFactoryBuilder, parsing mybaits. Nodes in XML
6. XMLConfigBuilder parses the mappers node and traverses the mapper path configuration list
7. Build XMLMapperBuilder, load the mapper configuration file obtained above, and call parse() to parse each node in the mapper configuration file.
8. In XMLMapperBuilder, add the path "mapper/UserMapper.xml", namespace, Class reflected by namespace, etc. to the Configuration
9. XMLMapperBuilder calls mapperregistry Addmapper (class < T > type), build MapperProxyFactory and register with Mapper known manager
9. SqlSessionFactoryBuilder constructs DefaultSqlSessionFactory through the Configuration created above

SqlSession sqlSession = getSessionFactory().openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.select(1L);

Using the SqlSessionFactory obtained above, first open the session to get SqlSession, then get UserMapper from SqlSession, and finally call UserMapper's method. Here we need to pay attention to how Mapper is surrogate.
1. Call the openSession() method of DefaultSqlSessionFactory, get the actuator type first, and then open the Session through the type
2. Create transactions and executors, build SqlSession and return
3. Getting Mapper from SqlSession through Class is extremely important. In fact, get MapperProxyFactory proxy factory from Configuration, call newInstance to create proxy Class mapperproxy < T > and return
5. The method of calling UserMapper is actually calling the proxy object method. We mainly need to pay attention to the invoke method of mapperproxy < T >
6. When the Mapp interface has an implementation class, it is called directly by invoke. If there is no implementation class, it is first taken from the methodCache. If it is not taken, the MapperMethod is built and put in
7. Execute according to the query type of addition, deletion and modification, call the corresponding execution, and return the package structure

 

Topics: Java Programming Programmer