终于谈到了Mybatis最核心的东西了,最核心的就是通过配置XML文件或注解中的SQL,直接调用接口就能执行配置好的SQL语句并封装成对应的返回类型的数据。
先看一下Mybatis使用示例:
//创建Builder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//读取配置文件IO流
InputStream is = Resources.getResourceAsStream("mybatis/mybatis-cfg.xml");
//解析IO流到内存Configuration对象中
SqlSessionFactory build = builder.build(is);
//获取一个持久化会话对象
SqlSession openSession = build.openSession();
//通过SqlSession调用
List list = openSession.selectOne("com.test.mybatis.MybatisTest.official.dao.IUserDao.getUserInfo");
System.out.println("============"+list);
//通过接口对象调用
IUserDao dao = openSession.getMapper(IUserDao.class);
System.out.println(dao.getUserLimit());//关闭SqlSession
openSession.close();
虽然看起来使用非常简单,但是内部构建这么一个ORM框架还是很艰难的,所以本章节会特别长
设计模式
照旧先来看一看设计模式
建造者模式(Builder Pattern)
使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
UML:
Builder:给出一个抽象接口,以规范产品对 象的各个组成成分的建造。这个接口规定要实 现复杂对象的哪些部分的创建,并不涉及具体 的对象部件的创建;
ConcreteBuilder:实现Builder接口,针对 不同的商业逻辑,具体化复杂对象的各部分的 创建。 在建造过程完成后,提供产品的实例;
Director:调用具体建造者来创建复杂对象的 各个部分,在指导者中不涉及具体产品的信息, 只负责保证对象各部分完整创建或按某种顺序 创建;
Product:要创建的复杂对象。
建造者模式对齐工厂模式创建出来的对象要更加的复杂,适用于实例化对象频繁改变的场景,适合流式编程。
但是现在流式编程是趋势,所以使用建造者模式创建对象的框架会越来越多,下面会讲到流式编程。
策略模式(Strategy Pattern)
一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
应用最多的就是if else代码当中
UML:
Context:算法调用者,使用setStrategy方法灵活的选择策略(strategy);
Strategy:算法的统一接口;
ConcreteStrategy:算法的具体实现;
源码分析
构建并解析XML文件
在使用的过程中都了解到了,首先就是将Mybatis的配置XML文件进行解析,解析后的数据都是存于Configuration对象当中,
先看一下Configuration这个类,单例,生命周期是应用级别,属性太多这里就不全截图出来了,大家可以自己下载源码去查看,截图如下:
但是在这个类中有几个对象需先讲一下:
MapperRegistry
mapper接口动态代理工厂类的注册中心。在MyBatis中,通过mapperProxy实现 InvocationHandler接口,MapperProxyFactory用于生成动态代理的实例对象;
TypeAliasRegistry
所以别名存储的地方,但是大家可以去看一下Configuration的构造方法,各类的默认参数都已经设置进去,不仅仅是Configuration的构造,在它自己的构造中将jdk自带的一些类也加进去了,这就是为什么有些别名默认就有的原因:
这个类里面也是有个Map<String,Class<?>>的这样的map用来储存这些数据
mappedStatements
这个是所有SQL信息储存的地方,使用的事继承自HashMap的自定义类StrictMap,这个map的key值为mapper的XML的namespace加上select|update|delete|insert标签的ID。具体的下面会谈到。
SqlSource
mapper.xml文件中的sql语句会被解析成SqlSource对象,经过解析SqlSource包含的语 句最终仅仅包含?占位符,可以直接提交给数据库执行;
TypeHandlerRegistry
是将数据库中的类型转换成JDK中的类型,进行对应,这个也有默认的集合,大家也可以去源码里面看一下。
整体介绍
整体解析建造者Builder类关系图:
使用的建造者图解:
我们就跟着解析流程讲解一下(在创建XMLConfigBuiler对象的时候会把Configuration对象创建出来):
进XMLConfigBuilder对象的parse方法看,执行了一个parseConfiguration(parser.evalNode("/configuration"));解析对象之前封装的XNode节点数据;
有些并非很重要的方法就没有标明,所以感兴趣的可以去看一下。
解析properties标签
解析完后会将所有的数据设置在configuration对象当中。
解析typeAliases标签
解析plugin标签
插件的使用这好像好没有提到,等后面在说吧。
解析mappers标签
这个算是解析文件里的一个比较中的东西
这时候开始使用XMLMapperBuilder建造者开始建造数据了:
我们看一下configurationElement方法:
这里的最后一个方法就开始了XMLStatementBuilder建造者开始建造了,看一些比较关键的地方
在创建MappedStatement对象时,使用的是流式编程
将数据全部加载完后,就是绑定工作空间了
在绑定方法中,如果namespace不是指定的接口全类名,那么就不会被添加到 mapperRegistry对象当中,这个对象就是用来跟接口做绑定的,进入到mapperRegistry的addMapper方法当中,
基本就是这么将动态代理跟namespace绑定的,构造XML的过程基本完结了,使用Mybatis的初始化已经完成,接下来就是使用持久化会话SqlSession这一块了
创建SqlSession
SqlSession是Mybatis对外提供的一个重要接口,可以通过这个跟数据库进行交互,或者创建接口代理对象,
UML:
创建一个默认的SqlSession工厂,这里用到了抽象工厂设计模式,之前为博客中提到了抽象工厂,那么这里就不细讲了,
我们调用时创建出了一个默认的DefaultSqlSession对象,在之前的示例当中,我们可以直接调用selectList方法也能调用getMapper方法,其实直接使用SelectList方法是ibatis编程模型,当加入了getMapper方法后就是采用接口形式开发,这种开始模式比之前的ibatis开发模式要更为简洁,并清楚业务。
我们先看一下原始的ibatis编程,即调用namespace+id方法
我们在看一下getMapper编程模式:
创建除了动态代理类之后,我们看一下代理类的实现:
这里的代理有一定的版本问题,由于JDK8支持在接口中写方法主体,所以需做一层判断才能执行正确的方法。
又是在invoke中执行的是invoke方法,所以在PlainMethodInvoker这个对象,执行的是mapperMethod.execute,
所以由此看出,getMapper也是基于ibatis编程模式开发。
SqlSession基本讲完了,接下来就是Executor执行器了。
Executor执行器
这个用到是模板模式,我在AQS中谈到的设计模式也是模板设计模式,模板模式-设计模式
BaseExecutor:抽象类,实现了executor接口的大部 分方法,主要提供了缓存管理和事务管理的能力,其他 子类需要实现的抽象方法为:doUpdate,doQuery等方法;
这个有四个对应的实现类:
SimpleExecutor:默认配置,使用PrepareStatement对象访问数据库,每次访问都要创建新的 PrepareStatement对象;
ReuseExecutor:使用预编译PrepareStatement对象访问数据库,访问时,会重用缓存中的statement对象;
BatchExecutor:实现批量执行多条SQL语句的能力;
ClosedExecutor:异常执行器。
默认的方法为SimpleExecutor(在Configuration参数中有),之前看到是执行了Executor中的query方法,这里讲一下:
进入到queryFormDatabase方法中:
在doQuery方法中,发现多了Executor也并非执行,让Handler去执行:
通过对SimpleExecutor doQuery()方法的解读发现,Executor是个指挥官,它在调度三个Handler工作:
StatementHandler:它的作用是使用数据库的Statement或PrepareStatement执行操作,启承上启下作用;
ParameterHandler:对预编译的SQL语句进行参数设置,SQL语句中的的占位符“?”都对应 BoundSql.parameterMappings集合中的一个元素,在该对象中记录了对应的参数名称以及该参数的相关属性
ResultSetHandler:对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型;
StatementHadnler
BaseStatementHandler:所有子类的抽象 父类,定义了初始化statement的操作顺序, 由子类实现具体的实例化不同的statement (模板模式);
RoutingStatementHandler:Excutor组件 真正实例化的子类,使用静态代理模式, 根据上下文决定创建哪个具体实体类;
SimpleStatmentHandler :使用statement 对象访问数据库,无须参数化;
PreparedStatmentHandler :使用预编译 PrepareStatement对象访问数据库;
CallableStatmentHandler :调用存储过程;
在获取到对应的Statement对象之后就是执行Statement并封装结果集
随着Executor执行器执行完毕,数据也就这样执行出来了。
Mybatis大部分的核心知识基本都在这里,但是难免还是会有一些遗漏;后面遇到了就会做一些补充。
很多插件以及可扩展的方法接口等等,Mybatis发展这么多年,不可能在一段时间内知晓,还需潜心学习。