Java Blog程序员

MyBatis使用以及源码浅析

2019-03-09  本文已影响17人  爪哇部落格

MyBatis是一个普遍应用并且十分优秀的持久层框架;本文将简单介绍MyBatis的使用,同时也将分析其在代码层面是如何实现的;本文的演示环境如下:

JDK1.8

mysql 8.0.15

MyBatis 3.4.6

MyBatis的使用

构建SqlSessionFactory

每个基于MyBatis的应用都是以SqlSessionFactory为核心的,SqlSessionFactory可以通过SqlSessionFactoryBuilder来构建;常用的有两种方式,一种是通过XML配置构建,而另一种则是通过Configuration实例对象来构建;

通过XML构建:

XML配置文件内容如下:

通过Configuration实例对象来构建:

获取到SqlSessionFactory以后,我们便可以获取到SqlSession,最后便可以执行已经映射的SQL语句了;具体代码如下:

执行SQL语句

执行SQL语句的方法也并非只用一种,亦可以直接通过SqlSession来执行

通过SqlSession执行

通过获取Mapper来执行

随后会在下文针对映射语句,执行语句进行较为详细的介绍。

映射的SQL语句

当然,映射的SQL语句也是不一定必须通过XML文件来完成的,也是可以通过注解来实现的

对于简单的SQL语句,通过注解的方式,你会觉得十分的得心应手;然而实现复杂的SQL则会有些混乱和力不从心;使用者可以根据自己的需求来使用,而不用单单被局限于其中的某一种形式。

作用域以及生命周期

依赖注入框架会创建线程安全的SqlSession和Mapper并将它们注入到你的bean中,因此你可以直接忽略它们的生命周期;如何通过依赖注入框架来使用MyBatis,后面我们将会介绍;你也可以参看MyBatis-Spring。

SqlSessionFactoryBuilder

这个类可以被实例化,创建以及销毁,一旦在其创建SqlSessionFactory,其实就不需要它了;所以它最好的作用域就是方法作用域(局部方法),当然你可以通过它创建多个SqlSessionFactory实例,当然最好不要让SqlSessionFactoryBuilder一直存在,以保证XML资源被用于更重要的事情。

SqlSessionFactory

SqlSessionFactory一旦被创建就应该在应用运行期间一直存在;它不应该被频繁的销毁创建,所以它的作用域应该是应用作用域;最好通过单例模式来使用。

SqlSession

每个线程都应该有自己的SqlSession,SqlSession不是线程安全的,所以它不能被线程共享,所以SqlSession不能被置于静态类,或者一个类的实例变量;所以它的作用域最好是请求或者方法。例如在HTTP请求中,应该每次收到一个请求,便打开一个SqlSession,响应之后,立即关闭SqlSession。

Mapper 实例

通过使用的Demo,我们也知道Mapper 实例是通过SqlSession获得的,所以它的作用域也是方法作用域。

以上MyBatis使用指南,主要参考MyBatis官方文档

源码解读

了解以上知识,让我们对MyBatis有了进一步的了解;便于我们捕捉源码的阅读方向;我们知道SqlSessionFactory是通过SqlSessionFactoryBuilder来构建的,接下来我们首先来看看它;

SqlSessionFactoryBuilder

我们看到SqlSessionFactoryBuilder中有这各式各样的build方法;最终都调用到如下方法:

以上代码也印证了我们主要介绍的两种方法来构建SqlSessionFactory,最终会返回一个DefaultSqlSessionFactory实例。

SqlSessionFactory

我们可以看到SqlSessionFactory主要提供了开启SqlSession以及获取Configuration的方法;知道了接口作用,我们再来看看默认的实现类DefaultSqlSessionFactory。获取SqlSession最终都交给了两个私有的方法:openSessionFromDataSource,openSessionFromConnection;顾名思义分别是通过数据源来获取,通过连接来获取;两个方法大同小异,我们来详细看看其中一个。

以上便是创建SqlSeesion的过程,利用public DefaultSqlSession(Configuration configuration, Executor executor) 此构造方法来获取SqlSession实例。

SqlSession

我们发现SqlSession提供了所有对数据库的操作,各式各样的增删改查,以及获取映射Mapper的方法;接下来我们仔细研读一下默认的实现类DefaultSqlSession

我们发现在DefaultSqlSession实现类中SqlSeesion封装的对数据库的操作最终都是有Executor来执行的;相当于SqlSeesion提供对数据库相应的操作,而具体的职责是有Executor来完成的。

Executor

下图是Executor的主要功能

下图是Executor的继承实现关系

Executor在SqlSessionFactory开启SqlSession时创建,代码片段如下:

找到Executor以后,我们离真相又近了一步;Executor从设计上就考虑到了缓存,我们可以从createCacheKey,clearLocalCache等方法看到其设计的巧妙之处;这里我们先忽略缓存设计(后面会做详细说明),我们先来看看它的抽象类BaseExecutor;

我们看到BaseExecutor对Executor接口进行了实现,最终调用抽象方法doXX;需要交由子类去实现。以doUpdate,doQuery为例,我们来看看子类实现。

最终MappedStatement都是由StatementHandler去执行,至此一个映射语句的完整执行流程就此结束。当然还有一个十分重要的类Configuration,我们再次必须强调一下,Configuration基本存在于整个流程,从通过SqlSessionFactoryBuilder构建SqlSessionFactory,到SqlSessionFactory中Executor的创建,开启SqlSession,再到映射语句的执行。

Configuration

还记得我们开篇就提到过Configuration实例有两种构建方式,一种是通过XMLConfigBuilder.parse方法,另一种是利用public Configuration(Environment environment)构造方法;所以Configuration从实质上来说就是XML的java对象表示。

这些元素在Configuration的成员变量中都可以找到。上图参考

可能有的同学会好奇Mapper文件是如何配置到Configuration中的,其实在构建Configuration实例的时候,调用的addMappers方法就将Mapper文件注册好了,我们通过以下代码一起来梳理一下

还记得上面执行语句的时候有两种方式,一种是通过SqlSession直接执行,一种是getMapper以后,调用具体的方法。

我们在DEBUG testQuery 时,发现执行mapper.selectBlog(101);有代理对象来执行,这是为什么呢?

这是因为我们在session.getMapper(BlogMapper.class); 获取的实际上是MapperRegistry中代理工厂生产的代理对象

通过对源码的分析认识,我们用一张流程图来总结整个流程

项目调试地址:https://github.com/sexylowrie/mybatis-teach

上一篇 下一篇

猜你喜欢

热点阅读