【Mybatis源码解析】- Mybatis入门操作
Mybatis入门
MyBatis
和Hibernate
一样,是一个优秀的持久层框架。已经说过很多次了,原生的jdbc
操作存在大量的重复性代码(如注册驱动,创建连接,创建statement
,结果集检测等)。框架的作用就是把这些繁琐的代码封装,这样可以让程序员专注于sql
语句本身。
MyBatis
通过XML
或者注解的方式将要执行的sql
语句配置起来,并通过java
对象和sql
语句映射成最终执行的sql
语句。最终由MyBatis
框架执行sql
,并将结果映射成java
对象并返回。
Mybatis的执行流程
我们大致了解一下Mybatis
的执行流程,结合案例工程来了解一下他是怎么工作的,后面将通过一步步的分析源码的方式进行深一步的了解
1、读取Mybatis配置信息
包含全局配置文件和映射文件。全局配置文件包含来数据源、日志、事务等信息,映射文件包含来SQL
执行相关的信息
2、获取SqlSessionFactory工厂
- 通过
XmlConfigBuilder
加载配置信息,例如properties
外部文件,alias
别名,plugins
插件等信息 - 通过
XmlMapperBuilder
加载Mapper
配置文件信息 - 处理每一个
mapper
内的信息。 - 通过
XmlStatementBuilder
处理标签为select
|insert
|update
|delete
的语句,最终调用addMappedStatement
方法,将mapper
配置文件中的每一条SQL
语句封装成mappedStatement
对象,作为value
保存在HashMap
集合中; - 下一步执行
addLoadedResource
使用HashSet
集合存放mybatis
的mapper.xml
映射文件路径地址; - 进入
bindMapperForNamespace()方法
,通过namespace
使用Java
反射机制找到mapper
接口,再调用addMapper()
方法,判断是否是接口类型,是否注册过(注册过则抛出异常)其中mapperRegistry
通过HashMap
保存mapper
接口,【key:接口;value:MapperProxyFactory】
3、获取SqlSession
- 进入
openSession()
方法,执行newExecutor()
方法创建执行器; - 先创建
SimpleExecutor
简单执行器,再判断是否开启了二级缓存,默认是开启的,就会去创建CacheExecutor
缓存执行器 - 执行
interceptorChain.pluginAll()
方法,责任链设计模式,底层使用动态代理技术,使开发者可以自定义插件开发,只需要实现Interceptor
接口,并指定想要拦截的方法签名即可,最后返回执行器;
4、操作Mapper接口
- 调用
getMapper()
方法,最终执行mapperProxyFactory.newInstance(sqlSession)
方法创建代理类MapperProxy
; - 执行
MapperProxy
代理类的invoke()
方法; - 判断
mapper
接口是否有实现类,显然我们没有实现类,则调用cacheMapperMethod()
方法去缓存中获取要代理的方法method
; - 进入
cacheMapperMethod()
方法先去查找缓存中有没有,没有的话将mapper
配置文件中配置的SQL
语句和对应的mapper
接口方法进行关联并放入map
缓存中,后期直接走缓存了,最后执行execute()
方法; - 执行
execute()
方法,最终调用select*()
方法; - 进入
selectList()
方法,调用getMapperStatement()
方法获取对应的SQL
语句; - 执行
query()
方法进行查询,判断如果开启了二级缓存并且配置了二级缓存存储介质(Redis,EhCache..)
则先走二级缓存中查询数据,第一次查询是没有缓存数据的,则刷新缓存配置,清除缓存。 - 二级缓存(
sessionFactory
)中没有查询到数据,就回去执行BaseExecutor
去查询HashMap
一级缓存中(sqlSession
)是否有缓存数据,一级缓存(PerpetualCache
)存放在内存中的,同理也是没有的,最后查询数据库DB
5、封装结果
案例
1、数据及Jar包准备
mybatis组件及mysql驱动
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
原始表数据
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for blog
-- ----------------------------
DROP TABLE IF EXISTS `person`;
CREATE TABLE `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
`mobile` varchar(11) COLLATE utf8mb4_general_ci DEFAULT NULL,
`age` int(2) DEFAULT NULL,
`email` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of blog
-- ----------------------------
BEGIN;
INSERT INTO `person` VALUES (1, 'wujiwen', '13011111111', 20, 'jiwenwu@outlook.com');
INSERT INTO `person` VALUES (2, 'mybatis', '13100000000', 10, 'service@mybatis.com');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
2、配置文件信息
全局配置文件信息mybatis-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"/>
<typeAliases>
<typeAlias type="cn.wujiwen.bean.Person" alias="person"/>
</typeAliases>
<environments default="development">
<environment id="development">
<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="PersonMapper.xml"/>
</mappers>
</configuration>
数据源信息
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis
username=root
password=passw0rd
映射文件Mapper.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="cn.wujiwen.mapper.PersonMapper">
<insert id="insert" parameterType="person" keyColumn="id" keyProperty="id">
insert into person (nickname,mobile,age,email) values (#{nickname},#{mobile},#{age},#{email});
</insert>
<update id="update" parameterType="person">
update person
<set>
<if test="nickname != null">
nickname = #{nickname},
</if>
<if test="mobile != null">
mobile = #{mobile},
</if>
<if test="age != null">
age = #{age},
</if>
<if test="email != null">
email = #{email},
</if>
</set>
where id = #{id}
</update>
<delete id="delete">
delete from person where id = #{id}
</delete>
<select id="listPerson" resultType="person">
select * from person;
</select>
<select id="getPerson" resultType="person">
select * from person where id = #{id}
</select>
</mapper>
Mapper接口
public interface PersonMapper {
int insert(Person person);
int update(Person person);
int delete(Integer id);
List<Person> listPerson();
Person getPerson(Integer id);
}
测试
/**
* Desc:
*
* @author wujw
* @email jiwenwu@outlook.com
* @date 2021/4/2
*/
public class MybatisStarted {
@Test
public void test() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
// 指定key获取sql
Person person = ((Person) session.selectOne("cn.wujiwen.mapper.PersonMapper.getPerson", 1));
System.out.println(person);
// 通过代理方式
PersonMapper mapper = session.getMapper(PersonMapper.class);
List<Person> personList = mapper.listPerson();
System.out.println(personList);
}
}
作者:黑米面包派
同步更新: https://www.wujiwen.cn
欢迎一起交流进步