简介
你了解MyBatis多少?了解SqlSessionFactory构建过程吗?了解XML解析过程吗?了解Mapper代理都干了什么事儿吗?本章以MyBatis 3.5.3带你一起阅读源代码,让你不在局限于会用,而是领会MyBatis的精髓。
通过简单Mybatis工程一步一步深入
Maven依赖包
<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 实体类
目录:com\user\User.java
@Data
public class User implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
private Long userId;
/**
* 手机号
*/
private String mobile;
}
UserMapper 接口
目录: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 配置文件
目录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 配置文件
目录: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配置文件
目录:resources\jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://192.168.1.100:3306/mybaits
username=root
password=root
Log配置文件
目录: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
数据库
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
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'
简要解析
String resource = "mybaits.xml";
Reader reader = Resources.getResourceAsReader(resource);
return new SqlSessionFactoryBuilder().build(reader);
先使用Resources.getResourceAsReader(resource)加载配xml置文件,然后SqlSessionFactoryBuilder的build方法构建SqlSessionFactory,这里主要关注SqlSessionFactory的构建过程。
1、SqlSessionFactoryBuilder委托XMLConfigBuilder
2、XMLConfigBuilder创建 XMLMapperEntityResolver DTD加载器,并委托XPathParser解析器解析mybaits.xml配置文件
3、XPathParser解析器使用XPathFactory加载xml和DocumentBuilderFactory构建Document
4、XMLConfigBuilder使用第三步的结果创建Configuration,并给Configuration赋值,最后完成XMLConfigBuilder的创建
5、SqlSessionFactoryBuilder中调用XMLConfigBuilder的parse()解析方法,解析mybaits.xml中各个节点
6、XMLConfigBuilder解析mappers节点,遍历mapper路径配置列表
7、构建XMLMapperBuilder加载上面得到的mapper配置文件,并调用parse()解析mapper配置文件中各个节点。
8、XMLMapperBuilder中把路径"mapper/UserMapper.xml"、namespace、namespace反射得到的Class等加入Configuration
9、XMLMapperBuilder调用MapperRegistry.addMapper(Class<T> type),构建MapperProxyFactory向Mapper已知管理器中注册
9、SqlSessionFactoryBuilder通过上面创建的Configuration构造DefaultSqlSessionFactory
SqlSession sqlSession = getSessionFactory().openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.select(1L);
使用上面获得的SqlSessionFactory,先打开会话获取SqlSession,然后从SqlSession中获取UserMapper,最后调用UserMapper的方法,这里需要关注Mapper是怎么被代理的
1、调用DefaultSqlSessionFactory的openSession()方法,先获取执行器类型,然后通过类型打开Session
2、创建事务和执行器,构建SqlSession并返回
3、通过Class从SqlSession中获取Mapper,此过程及其重要。实际上是从Configuration中获取MapperProxyFactory 代理工厂,调用newInstance创建代理类MapperProxy<T>并返回
5、调用UserMapper的方法,实际上是调用代理对象方法,主要需要关注MapperProxy<T>的invoke方法
6、Mapp接口有实现类时直接invoke调用,没有实现类时先从methodCache中取,没取到构建MapperMethod并放入
7、按增删改查类型执行调用相应的执行,并包装结构返回