Framework-MyBatis source code step by step (1)

Introduction

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

Step by step through 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

@Datapublic class User implements Serializable {    private static final long serialVersionUID = 1L;    /**     * 用户ID     */    private Long userId;    /**     * 手机号     */    private String mobile;}

UserMapper interface

Directory: com\user\UserMapper.java

public interface UserMapper {    /**     * 查询     */    User select(Long userId);    /**     * 插入     */    int insert(User user);    /**     * 修改     */    int update(User user);    /**     * 删除     */    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 configuration file

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.Driverurl=jdbc:mysql://192.168.1.100:3306/mybaitsusername=rootpassword=root

Log configuration file

Directory: resources\log4j.properties

log4j.rootLogger=DEBUG, stdoutlog4j.logger.org.mybatis.example.BlogMapper=TRACElog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

database

CREATE TABLE `user`  (  `user_id` bigint(19) NOT NULL COMMENT '用户ID',  `mobile` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '手机号',  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 = '用户' 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 ConnectionDEBUG [main] - Created connection 1579132337.DEBUG [main] - Setting autocommit to false on JDBC Connection [[email protected]]DEBUG [main] - ==>  Preparing: select user_id, mobile from `user` where user_id = ? DEBUG [main] - ==> Parameters: 1(Long)DEBUG [main] - <==      Total: 1User(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);

First use Resources.getResourceAsReader(resource) to load the configuration xml configuration file, and then build the SqlSessionFactory with the build method of SqlSessionFactoryBuilder. Here we mainly focus on the construction process of SqlSessionFactory.
1. SqlSessionFactoryBuilder entrusts XMLConfigBuilder
2. XMLConfigBuilder creates XMLMapperEntityResolver DTD loader, and entrusts XPathParser parser to parse the mybaits.xml configuration file
3. XPathParser parser uses XPathFactory to load xml and DocumentBuilderFactory to build Document
4. XMLConfigBuilder uses the result of the third step to create Configuration, And assign a value to the Configuration, and finally complete the creation of XMLConfigBuilder
5. Call the parse() parsing method of XMLConfigBuilder in SqlSessionFactoryBuilder to parse each node in mybaits.xml
6. XMLConfigBuilder parses the mappers node, traverses the mapper path configuration list
7, builds XMLMapperBuilder and loads the above obtained The mapper configuration file is called and parse() is called to parse each node in the mapper configuration file.
8. In XMLMapperBuilder, add the path "mapper/UserMapper.xml", namespace, and the Class obtained by namespace reflection into the Configuration
9. XMLMapperBuilder calls MapperRegistry.addMapper(Class<T> type) to build MapperProxyFactory to register with Mapper known managers
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 obtain the SqlSession, then obtain the UserMapper from the SqlSession, and finally call the UserMapper method. Here you need to pay attention to how the Mapper is proxied.
1. Call the OpenSession() method of the DefaultSqlSessionFactory, first obtain the actuator type, Then open Session by type
2. Create transaction and executor, build SqlSession and return
3. Get Mapper from SqlSession through Class, this process is extremely important. Actually get the MapperProxyFactory proxy factory from the Configuration, call newInstance to create the proxy class MapperProxy<T> and return
5. Call the UserMapper method, actually call the proxy object method, mainly need to pay attention to the invoke method of MapperProxy<T>
6. Mapp When the interface has an implementation class, it is directly invoked. If there is no implementation class, it is taken from the methodCache first, and the MapperMethod is not taken and placed in
7. According to the addition, deletion, modification, and check type execution, the corresponding execution is called, and the packaging structure returns