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