什么是Mybatis
CRUD框架,面向数据库开发的脚手架。它提供了支持CRUD操作,还具有以下特性。
Mybatis这套框架的核心在于遵循到位了开闭原则,里氏替换原。组件思想,抓核心思想等。
Mybatis分为三大组件:SQL组装层,数据处理转换层,插件管理层。
Mybatis的核心对象有:BoundSQL,Configuration,SqlSession,MapperProxy,StatementHandler.
一张图描述下查询List的几个步骤
查询在mybatis大概执行过程,"↓"表示下一个执行片段。
SqlSessionFactory
↓ 创建SqlSesssion,SqlSession相当于Mybatis的执行引擎 预装载datasource,进入configuration,
注意Datasource如果是在依赖注入容器里,也是Singletone模式的
Configuration
根据ID在configuration里面找到相应的命名空间维度的配置项,Mybatis设计上基本都是一次初始永久使用的,交给mapperProxy代理类去处理,MapperProxy处理类由它的工厂类MapperProxyFactory创建
↓
MapperProxy 执行invoke,这里面有个cache机制
↓
找到MapperMethod触发execute,里面有根据SQLCommandType去路由到不同的处理方式
↓
SqlSession insert() or update()
↓
Plugin 是否有Plugin机制
↓
RoutingStatementHandler 路由处理
↓
ParameterHandler 执行参数处理,
↓
ResultHandler 结果集处理,游标处理。映射成返回对象
↓
DebuggingPrintLog 有开启Debug日志才会代理Stmt 或者Psmt或者 Result代理
↓
TransactionHandler 关闭事物,如果有Spring食物切面就会托管给它去做这个事情
↓
Spring事物代理切面处理器
*可拓展
*调用链短
*支持内存分页
*手动SQL,满足对SQL性能有要求场景
SQL支持面向语言用注解方式进行组装,或者用ONGL的方式将SQL逻辑表现在XML语言文档中
*Mybatis不像Hibernate是一个真正的持久化框架,HBT更像是弱化了SQL这一基础技能属性。MB的SQL始终是具备完整性的。
*该框架提供了别名机制,别名可以外部配置然后在运行阶段一次性加载到全局的configuration中
Mybatis代码非常精简,上手非常容易。源码理解程度除了ONGL这块比较抽象,其他部分都很容易理解,本文也是化烦为简地方式来讲解,适用于初学者和老手。Mybatis是Web开发里面最简单的框架,很容易了解到原理。
如何快速理解一套框架,我的建议是把它当做一个工具集,既然是工具,先理解它的构造和设计原理,先通过官网描述了解它实现了什么功能,这些功能分别是什么,然后可以以架构师的角度去了解它的作用场景。
当了解了以上这些后,再去庖丁解牛的方式去解开它的内部构造,一般高级语言实现的一个框架在代码的结构上都是分包的,接着去了解它每个包的作用域,然后再去抽象出它所用到的设计模式。考虑一下为什么要用到这个设计模式,当了解了这些之后,就可以上升到如何用好和拓展这个框架了。
*设计模式:
包装器(Wrapper关键字的类群体),JDK动态代理模式,Cglib代理模式。(MapperInterface类),静态工厂模式(SqlSessionFactory,
LoggingFactory,
MapperProxyFactory),建造者模式(包含关键字builder的类),策略模式(隐藏式设计模式,缓存策略模式,日志输出策略模式,StatementHandler模式,使用者不需要关注处理细节,只需要告诉Mybatis你要做什么,只需要体现使用者的行为即可),装饰器模式,ONGL中用到了组合模式(<if>,<else><choosen>等组合tag),Plugin衍生的使用拦截器场景使用的责任链设计模式等(拦截器就是All-in-List then execute-stepbystep-in-chainGroup)。
模拟通讯服务上的路由器的网络分发功能实现的策略模式:
通过RoutingStatementHandler类就是一个路由器,然后通过StatementType类型来决定跳转到不同的handler。
*包结构
排名不分先后顺序
annonations:注解SQL的,作用于SqlBuilder使用场景
binding: 实现Mapper的绑定,里面有个MapperRegister
builder:构造Mapper,解析Configuration的基础配置等
cache:缓存包,装饰器模式。比如与依赖注入控制反转框架Spring使用时要以外部注入装载的方式引入redis等缓存中间件。
cursor:游标,获取Jdbc游标的迭代位置
datasource:数据源[jndi数据源,普通数据源(BasicDatasource)],基于数据库驱动包的条件下,才能ping上对应的数据库,同时,需要设置链接,链接的必要属性一般都是要用户名和密码认证通过才可以。
下面是一个jdbc接口和数据库交互的大致过程~
加载jdbc驱动包->创建链接->开启会话->开启事物->创建statement->读场景游标方式迭代结果集->关闭statement->写场景关闭事物->_遇到异常回滚事物
exceptions:异常包,这部分使用到了是factory model。
executor:支持JDBC操作的入口包
plugin:插件包,通常基于插件机制可以实现分页,SQL执行效率上的性能监控。
IO:流处理
session: JDBCSession等
logging:日志组件,开启debug模式时,针对Statement和result进行了打印日志的代理拦截,不影响原来的流程,该怎么往下执行还得继续。
transaction:事务包,mybatis对事物这块简单地加工了一下,可以支持强制关闭操作等行为
parsing:sql语句转换包
scripting:脚本包
type: JDBC type handler
mapping: 映射包。鉴别器,boundSQL,resultMap,这个包里面涵盖了
sql属性维度,columnType维度,resultMap等维度与ONGL之间的映射模型。这里面几乎都是PlainObject,几乎没有行为透露和传播。
reflect:反射机制包,针对数据库字段提取出值,通过反射机制映射到property字段里。
获取反射类内部用了取出一次就缓存的动作的。
核心类
Confiuration:Mybatis的核心配置类,顶层类,xml配置项,由XMLConfigurationBuilder提供解析装载。它提供你全局获取ResultMap,获取MapMethed.
MapperRegister:注册Mapper类。
SqlSessionFactory:通过build创建sqlSession,需要自定义一个datasouce,
比如集成到Spring框架中时会先定义一个DataSource。
BaseExecutor:执行 insert,update,select
,delete操作。Mybatis严格按照jdbc的CRUD操作返回的类型进行一一对应设计。
MapperProxy:代理类,用jdk动态代理机制,创建字节码方式。XML可以不需要定义接口,但是定义接口就必须要它所包含的package全路径需要是正确的路径。
ResultTypeHandler:Jdbc中的ResultType映射成返回ResultType使用的Java类型。Mybatis定义了自己的JdbcType,为什么它不fixed对应的Java类型,这是基于开闭原则,因为用户拿到的type尤其是char这样的类型丰富多变。它有个TypeRegister机制,用户可以定义自己的type作用于rst.
SqlSesession:就是用来处理connection,connection的处理流程是:开启事物,创建stm或者pstm或者callstm,获取结果集返回的游标,关闭事物(遇到异常抛出来)
CacheProvider:缓存提供者,MB提供了namespace作用域缓存和sqlsession两种缓存作用域。用装饰者设计模式设计出来了多种不同方式的缓存机制。缓存是个多余的设计,不满足业务场景,一般业务层应用开发都单独使用缓存中间件。比如Spring+redis实现。如果业务上的数据只需要隔离数据库,可以实现它的cache接口,用redisCache,用静态方法获取静态注入的RedisSessionFacotory来使用。
,,,
doPut ();
clear();
update();
,,,
ResultMap:核心类,它的上层是namspace,namespace实际上是存在于xml中,ResultMap是结果集映射,它的是结构化的,语义上的表现结构是由property与associate,collection组成。这些ONGL语言标签由mybatis解析。RSTM它对应的是一个map容器存在。从另一个角度来说,该框架一部分是语义型,一部分是将语义结构化的部分用组件的方式一一抽离出来的。
它的Id是由上往下一直到parameter组装的字符串形式。它是放在一个Map容器的。当SqlSessionFactory完成build以后,mybatis需要的config->作用的对象,cache生成,日志形式等就随着build动作结束以后完成所有的初始化了。
StatementHandler:处理statement,jdbc基础知识,可以预处理,存储过程或函数处理。
PooledConnection:简单地实现了连接池,池化的关键是基于线程池创建管理数据库链接池,一方面减少线程上下文切换奢侈开支的同时又,另一方面又复用了数据库连接池。它里面会检测坏的连接池,坏链接会专门管理,复活会重新使用。对于它里面的设计只需要改进连接池队列和利用aqs来更高的处理就可以演化成一个好用的连接池。
*特性
1.事务性
2.SQL日志
SQL日志可以用拦截器的方式美化query和update的语句。
3.Plugin特性(指定签名方法进行拦截,可以实现SQL性能监控,分页,租户等机制),mybatis支持多个plugin同时存在,没有严格意义上的先后执行顺序,由定义顺序来决定的,业务上有需要的场景有:分页功能,数据加密,防止SQL注入,schema鉴定(比如mybatis拿到用户域以后可以指定用户的schema,SQL性能监控,慢查询直接中断查询,但是就算关闭链接,数据库的任务也不会终止)
4.缓存机制(namespace层,SqlSession粒度的缓存,一个是作用域在命名空间,一个作用域在session粒度).
关于缓存,Mybatis里面提供了LRU缓存机制,如果感兴趣的朋友可以自行实现。因为缓存不是Mybatis的重要属性
下面如何添加一个FILO缓存代码示例
importjava.util.Deque;
importjava.util.LinkedList;
importorg.apache.ibatis.cache.Cache;
/**
* FILO (first in, last out) cache decorator.
*
* @author mybatis
*/
publicclassFifoCacheimplementsCache{
privatefinalCachedelegate;
privatefinalDeque<object>keyList;</object>
privateintsize;
publicFifoCache(Cachedelegate) {
this.delegate=delegate;
this.keyList=newLinkedList<>();
this.size=1024;
}
@Override
publicStringgetId() {
returndelegate.getId();
}
@Override
publicintgetSize() {
returndelegate.getSize();
}
publicvoidsetSize(intsize) {
this.size=size;
}
@Override
publicvoidputObject(Objectkey,Objectvalue) {
cycleKeyList(key);
delegate.putObject(key,value);
}
@Override
publicObjectgetObject(Objectkey) {
returndelegate.getObject(key);
}
@Override
publicObjectremoveObject(Objectkey) {
returndelegate.removeObject(key);
}
@Override
publicvoidclear() {
delegate.clear();
keyList.clear();
}
privatevoidcycleKeyList(Objectkey) {
keyList.addLast(key);
if(keyList.size() >size) {
ObjectoldestKey=keyList.removeLast();
delegate.removeObject(oldestKey);
}
}
}
5.支持ONGL的SQL写法,支持SqlBuilder以注解形式组装 写SQL,然后表现形式 以显示在方法上
6.支持拓展,比如国产化的Mybatis-Plugin框架,这个框架就是改变MapperBuilder这个内部机制的基础上将单个表的增删改查方法嵌入在SqlMethod里面了。这个框架还实现了低代码思想,并支持Stream流式写法。
7.写了一个不支持高并发的链接池,低吞吐量用户量少时可用
一句话结束
Mybatis是JAVA-Web开发中国内最流行的一套面向数据库开发的框架,它是一款面向对象设计思想的框架。简单易上手,深受广大各个级别的程序员喜爱。
问:How to designe a new framework that is better than mybatis.
问:What is the lack functions of current mybatis-plugin
问:How to avoid restart the web application when some resources ref mybatis has changed by user
答:定义FileChangeListern事件,监听文件。先找到修改文件对应的MapperInterface,指定它重新注入mapper,用MapperRegister重新注册。