一、回顾Mybatis的使用
Mybatis应该是现在我们项目中使用非常频繁的框架,它几乎消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装,让我们可以使用简单的XML或注解用于配置和原始映射。
还记得我们在配置Mybatis的时候都要写一个 mybatis_config.xml
最常写的应该数据库连接信息,还有Mapper.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>
<!-- 环境配置 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- 数据库连接相关配置 ,这里动态获取config.properties文件中的内容-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://www.bixuechao.com:3306/testdb"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- mapping文件路径配置 -->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
上面配置了数据库连接信息,和mapping文件的路径,如果要查询数据库的信息,是不是还要再来个接口和 Mapper.xml 文件:
public interface UserMapper {
UserEntity getUser(int id);
}
<?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.mybatis.demo.mapper.UserMapper">
<select id="getUser" parameterType="int"
resultType="com.mybatis.demo.entity.UserEntity">
select * from user where id=#{id}
</select>
</mapper>
相信上面这种基本上大家都这样写过吧,那再来看下下面的代码是否熟悉:
public class TestMyBatis {
public static void main(String[] args) {
try {
// 基本mybatis环境
// 1.定义mybatis_config文件地址
String resources = "mybatis_config.xml";
// 2.获取InputStreamReaderIo流
Reader reader = Resources.getResourceAsReader(resources);
// 3.获取SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 4.获取Session
SqlSession sqlSession = sqlSessionFactory.openSession();
// 5.操作Mapper接口
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
UserEntity user = mapper.getUser(175);
System.out.println(user.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面这是直接使用Mybatis的方式调的mapper,大家在项目中应该大多都是SSM或者SpringBoot 已经封装的自动帮我们实现了上面的这种代码,在这里写上面这种形式更方便下面对源码的解读和大家的理解。
二、Mybatis 的执行原理流程
- 当项目启动的时候,会首先读取我们配置的
mybatis_config.xml
文件,然后读取文件转化为InputStreamReader
格式使用SqlSessionFactoryBuilder().build()
方法解析XML,并且mybatis_config.xml
文件只可被解析一次,如果手动再调用解析会报:Each XMLConfigBuilder can only be used once.
错误。 - 解析
mybatis_config.xml
文件时,会将settings、environments、mappers
等信息解析装配到Configuration
对象中,供其他地方使用,同时Mapper.xml
中的信息比如namespace
接口地址会使用反射拿到Class
,存放到 名为knownMappers
的HashMap
中,还有Mapper.xml
中的select|insert|update|delete
语句,会根据id
为key,value为MappedStatement
的对象存放到名为mappedStatements
的HashMap中。 - 解析完XML会返回一个
DefaultSqlSessionFactory
的对象。下面可以通过这个对象openSession()
方法拿到一个SqlSession
对象。 - 在获取
SqlSession
对象会根据cacheEnabled
创建执行器 ,注意Mybatis的二级缓存默认是开启的,也就是cacheEnabled
默认是为true
,所以这里默认创建的是二级缓存的CachingExecutor
,如果设成false,返回的便是SimpleExecutor
,现在好多网上说二级缓存默认关闭需要注意下,下面看源码的时候可以看下这个变量的值,至于在项目中为何二级缓存没有生效,可以检查下xml配置文件有没有<cache></cache>
声明或者是否手动把cacheEnabled
设置为了false
,在使用默认的二级缓存的时候 Mybatis 是采用的 HashMap作为缓存容器,在并发情况下还是比较容易发生线程安全问题,如果要使用二级缓存,可以使用Redis来缓存,配置到Mybatis中。 - 在上面已经拿到一个
Executor
执行器,通过执行器有为我们创建了一个DefaultSqlSession
返回出去。 - 在拿到了
SqlSession
后便可以获取Mapper接口对象了,SqlSession
中有个getMapper()
方法,通过传入一个Class 便可拿到该对象,是怎么实现的呢,其实就是使用的java的动态代理技术,做了个代理类,在第二点的时候我提到了一个knownMappers
的HashMap容器存放namespace
中的接口Class,现在在getMapper的时候就是根据传入的Class 取knownMappers
容器中取,取到之后 Mybatis 同过MapperProxy
类实现了InvocationHandler
代理了Mapper接口。 - 再通过
SqlSession
的getMapper()
方法上面已经拿到了Mybatis 给我们的代理对象,所以下面在执行对象中的方法时,肯定执行的时代理类中的invoke
方法,在这个方法中 Mybatis 又提供了一个MapperMethod
Mapper执行对象,来帮助我们执行下面的SQL操作,并且在这里中为了性能提升,还对MapperMethod
做了个简单的HashMap 缓存。 - 上面说道
MapperMethod
是帮我们执行了下面的SQL操作,其实还是调用了sqlSession
中的方法,如果是返回一个对象,就会使用sqlSession
的selectOne
方法执行,其实selectOne
方法还是又调用的selectList
。接着如果是一个查询操作,会使用Executor
执行器的query
方法,会先触发CachingExecutor
的query
方法先查询缓存这个缓存就是二级缓存,如果没有,会再去父类的BaseExecutor
的query
在这边会再查询一级缓存,还是没有就会去SimpleStatementHandler
这个类下的query
方法使用java 原声的Connection、Statement
去查询数据库,并将查询的数据整理成List类型返回,如果List中只有一个数据,就将第0个返回出去。 - 上面拿到了数据库的返回结果,会先存放到一级缓存中,如果二级没有关闭然后再放到二级缓存中,然后返还给最终数据给最初的调用者。
别看Mybatis我们使用的那么简单,其实执行原理还是挺多的,其中有很多是需要我们学习和借鉴的东西,如果对上面的流程不太明白,下面便是对源码的解读,方便我们对上面流程的理解。
三、Mybatis源码解读
未完待续。。。