<h2>前言<br>
</h2>
<p>Mybatis 是 Java 开发中比较常用的 ORM 框架。在日常工作中,我们都是直接通过 Spring Boot 自动配置,并直接使用,但是却不知道 Mybatis 是如何执行一条 SQL 语句的,而这篇文章就是来揭开 Mybatis 的神秘面纱。<br>
</p>
<h2>基础组件<br>
</h2>
<p>我们要理解 Mybatis 的执行过程,就必须先了解 Mybatis 中都有哪一些重要的类,这些类的职责都是什么?<br>
</p>
<p><strong>SqlSession</strong><br>
</p>
<p>我们都很熟悉,它对外提供用户和数据库之间交互需要使用的方法,隐藏了底层的细节。它默认是实现类是 DefaultSqlSession<br>
</p>
<p><strong>Executor</strong><br>
</p>
<p>这个是执行器,SqlSession 中对数据库的操作都是委托给它。它有多个实现类,可以使用不同的功能。</p>
<p style="text-align: center"><img alt="" src="https://beijingoptbbs.oss-cn-hangzhou.aliyuncs.com/jb/2426819-d282a9e35ef9b33a453b7fe88cabac61"></p>
<p><strong>Configuration</strong><br>
</p>
<p>它是一个很重要的配置类,它包含了 Mybatis 的所有有用信息,包括 xml 配置,动态 sql 语句等等,我们到处都可以看到这个类的身影。<br>
</p>
<p><strong>MapperProxy</strong><br>
</p>
<p>这是一个很重要的代理类,它代理的就是 Mybatis 中映射 SQL 的接口。也就是我们常写的 Dao 接口。<br>
</p>
<h2>工作流程<br>
</h2>
<h3>初步使用<br>
</h3>
<p>首先,我们需要得到一个 SqlSessionFactory 对象,该对象的作用是可以获取 SqlSession 对象。<br>
</p>
<div class="blockcode">
<pre class="brush:java;">
// 读取配置
InputStream resourceAsStream = Resources.getResourceAsStream("config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 创建一个 SqlSessionFactory 对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
</pre>
</div>
<p>当我们得到一个 SqlSessionFactory 对象之后,就可以通过它的 openSession 方法得到一个 SqlSession 对象。<br>
</p>
<div class="blockcode">
<pre class="brush:java;">
SqlSession sqlSession = sqlSessionFactory.openSession(true);
</pre>
</div>
<p>最后,我们通过 SqlSession 对象获取 Mapper ,从而可以从数据库获取数据。<br>
</p>
<div class="blockcode">
<pre class="brush:java;">
// 获取 Mapper 对象
HeroMapper mapper = sqlSession.getMapper(HeroMapper.class);
// 执行方法,从数据库中获取数据
Hero hero = mapper.selectById(1);
</pre>
</div>
<h2>详细流程<br>
</h2>
<h3>获取 MapperProxy 对象<br>
</h3>
<p>我们现在主要关注的就是 getMapper 方法,该方法为我们创建一个代理对象,该代理对象为我们执行 SQL 语句提供了重要的支持。<br>
</p>
<div class="blockcode">
<pre class="brush:java;">
// SqlSession 对象
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
</pre>
</div>
<p>getMapper 方法里面委托 Configuration 对象去获取对应的 Mapper 代理对象,之前说过 Configuration 对象里面包含了 Mybatis 中所有重要的信息,其中就包括我们需要的 Mapper 代理对象,而这些信息都是在读取配置信息的时候完成的,也就是执行sqlSessionFactoryBuilder.build 方法。<br>
</p>
<div class="blockcode">
<pre class="brush:java;">
// Configuration 对象
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
</pre>
</div>
<p>我们可以看到它又将获取 Mapper 代理对象的操作委托给了 MapperRegistry 对象(搁着俄罗斯套娃呢?),这个 MapperRegistry 对象里面就存放了我们想要的 Mapper 代理对象,如果你这么想,就错了,实际上,它存放的并不是我们想要的 Mapper 代理对象,而是 Mapper 代理对象的工厂,Mybatis 这里使用到了工厂模式。<br>
</p>
<div class="blockcode">
<pre class="brush:java;">
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public MapperRegistry(Configuration config) {
this.config = config;
}
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
|
|