MyBatis技术内幕读书笔记

最近花了两三天把我以前看过的《MyBatis技术内幕》又看了一遍,感觉这次受益匪浅,整本书从基础到上层讲解,对于各种xml等基础知识点也扩展了一堆,又在必要时设计模式神插入,就认真的做了一下笔记。

不过最近才开始做源码笔记,不太清楚怎么写

输入参数类型

可以是Map、List 等集合类型,也可以是基本数据类型和 POJO 类型

Mybatis的一级、二级缓存

一级缓存与session绑定

二级缓存与Namespace绑定

所有缓存在对应域进行增删改后 都会被清空

image-20210713195739051

openSession

创建执行器(三种:普通,重用,批量)

image-20210714105708329

代理执行,不拦截Object中的方法

image-20210714105916399

session.getMapper创建代理

image-20210714110021443

拦截执行代理

image-20210714110254013

最终还是根据方法签名调用到session.selectOne

image-20210714110351059

查询前会使用缓存

image-20210714110559517

执行流程

image-20210714111335209

#{}和${}的区别

在配置文件中${}用于引用属性文件

在sql中引用变量#{}会使用?占位符,${}会直接拼接

MyBatis默认使用RowBounds进行分页,非物理分页,会查询所有再分页

属性名与列名不同映射

对于属性名的大小写会直接忽略,若还是不同,可以开启驼峰命名法,若还是不同,可以使用resultMap手动映射,或sql查询起别名

Mybatis分层

image-20210806101929274

xml文档解析

DOM需要全部读取进内存 SAX通过流式回调处理,需要程序员自己维护层级逻辑 StAX采用拉模式,程序通过调用解析器进行解析

XPathParser封装了XML解析逻辑

image-20210806103400264

通过流文件构建文档

image-20210806103547834

用于解析各种数据类型(都是通过先解析为String再转化过去)

用于处理有占位符的文件

image-20210806103725870

image-20210806104209189

简单使用

存储xml节点类,会在创建时解析属性与内容(就是内部循环解析)

image-20210806104807032

反射工具封装

image-20210806105009825

Method#isBridge() 判断方法是否为虚拟机自动生成的

image-20210806105722353

用于返回方法的唯一标识

image-20210806110049939

防止子类重写父类的Getter方法造成一个属性多个Get方法,只保留一个(Set类似)

Invoker封装常见方法的调用(基本都是简单调用Method#invoke)

image-20210806110605147

ReflectorFactory提供对Reflector的缓存与创建

image-20210806110939355

唯一实现类,非常简单

Type所有类型的父接口

image-20210806111049671

MyBatis内部使用TypeParameterResolver提供的静态方法对Type处理

image-20210806112815953

对象工厂

image-20210806112940666

第一个create使用无参构造器,第二个使用有参构造器

DefaultObjectFactory是其唯一实现类

image-20210806113206456

解析对应接口要返回的实际类型,create方法实现就是上面说的非常简单

属性解析工具类

image-20210806113500366

负责解析name[0].age[1].id这种有" [ ] . "存在的属性

image-20210806113753674

负责方法名到属性名转换的工具类

image-20210806114020680

PropertyCopier负责对对象属性递归拷贝

image-20210806114225721

封装对象的元数据信息,默认使用静态方法构建

ObjectWrapper负责对对象的封装,使用ObjectWrapperFactory创建

image-20210806115341557

BaseWrapper主要实现了集合的操作

image-20210806115746812

对对象数据再封装

image-20210806120704833

可以使用TypeHandler对自定义类型进行映射

image-20210806121118648

BaseTypeHandler实现TypeHandler做了非常简单的封装(跟直接用原来的几乎没有区别)

用于管理TypeHandler(还默认注册了大量TypeHandler)

image-20210806121324298

根据注解,或接口信息确定注册的TypeHandler类型

image-20210806121644208

根据转换类型获取TypeHandler

image-20210806122422136

MyBatis类型转换使用Map缓存查找式,Spring类型转换使用责任链轮询式

类型别名注册,默认已经注册了大量别名

image-20210806122952544

image-20210806123116656

别名默认为简单类名,可以使用注解指定

日志模块只是对其他日志实现进行简单的封装

image-20210806123707402

懒加载原理

使用代理,在真正需要数据时,再调用代理获取数据

用于格式化打印sql日志

image-20210806124614359

主要记录的要跟踪打印的方法,sql执行层数(用于格式化sql)

通过代理对Sql日志打印

image-20210806124840411

监听连接创建Statement等的方法返回对应的代理对象

image-20210806125430018

代理对象对感兴趣的方法返回代理结果集

image-20210806125715911

最终在ResultSetLogger代理结果中输出格式化sql,结果,前面

Tomcat的自定义类加载器

image-20210806132728021

Tomcat为每个部署的应用创建一个唯一的WebAppClassLoader,它负责加载应用自己的jar包与class文件,使不同Web应用相互隔离,热部署时,丢弃原来的ClassLoader创建新的,父类都是CommonClassLoader可以共享其中的类库

ResolverUtil通过Test寻找合适的类

image-20210806133456279

image-20210806133614240

Test主要实现类,根据类型判断(IsA),根据注解判断(AnnotatedWith)

VFS用于遍历路径资源,优先添加用户自定义的

image-20210806144048037

默认实现有JBoss6VFS与DefaultVFS

数据源工厂主要有池化与非池化

image-20210806144624577

image-20210806144741297

setProperties用于设置属性,getDataSource用于获取数据源(上图UnpooledDataSourceFactory的实现,池化的只是返回对象不同)

image-20210806145518809

非池化数据源,每次都创建新连接(先初始化驱动,再获取连接,最后配置连接的自动提交,隔离级别等属性)

image-20210806145919343

image-20210806145955665

image-20210806150024637

池化数据库返回的是一个代理,记录各种时间戳完成长时间空闲关闭等,在调用关闭时会被拦截放入连接池,并非关闭

image-20210806150920687

image-20210806150951908

判断是否有效,最终会执行一条PING语句

image-20210806163850695

对活跃连接,空闲连接全部回滚关闭(空闲连接与这类似)

事务Transaction的两个实现类

image-20210806164132859

JdbcTransaction只是对原来连接方法的简单封装,后者commit与rollback都是空实现依赖容器实现

MapperRegistry用于注册接口映射

image-20210806164955371

image-20210806165053242

获取Mapper代理

MapperProxyFactory主要创建代理

image-20210806165352303

image-20210806165218307

还有对应的方法接口信息

MapperProxy处理具体代理逻辑

image-20210806165608799

会放行默认Object方法,对方法调用信息进行缓存

Mapper接口与xml配置文件的桥梁

image-20210806165857021

SqlCommand记录Sql语句与其类型,MethodSignature记录接口方法信息,执行时根据sql类型选择执行

ParamNameResolver负责解析方法参数

image-20210806170534003

image-20210806170548302

参数解析会过滤掉RowBounds与ResultHandler,会先尝试获取注解,再获取方法名还是为null直接使用map大小

image-20210806170906952

根据具体参数值与解析的参数信息,封装命名参数对象,方便后面使用,注意0,1,2等会被注册为param1等

MapperMethod核心方法execute

image-20210806171425267

INSERT,UPDATE,DELETE都类似,都是先转换参数再调用对应的insert,delete等最后包装返回

SELECT稍微麻烦需要,根据返回值封装返回对象

image-20210806171737610

rowCountResult根据影响行数与返回结果包装返回null,int,long,boolean

image-20210806173512639

对与SELECT若参数中有ResultHandler,会优先调用它处理返回结果,其中会判断是否有RowBounds参数

SELECT返回值转换为其他类型也都要RowBounds参数是否存在的判断

上面的方法最终都是调用SqlSession的数据库操作方法

缓存使用装饰器

image-20210806174939348

Cache有很多实现类,但只有PerpetualCache实现了逻辑(简单的Map缓存),其他都是装饰器(构造器要求必须传入包装Cache)

MyBatis缓存依据多个因素使用CacheKey控制

image-20210806180315364

解析使用建造器模式

image-20210807075553130

核心属性依次为:全局配置(最重要,后者都来源于它),类型别名注册器,类型处理注册器

XMLConfigBuilder上面的子类负责解析逻辑

image-20210807075810843

解析各种节点属性

image-20210807075908840

解析properties配置文件

image-20210807080051747

MyBatis真是纠结症,遇到不确定的不能选个默认规则,直接报错

处理设置为属性类

image-20210807080410138

注册别名

image-20210807080544827

在registerAlias中会自动处理注解

注册类型处理器(与上面类似)

image-20210807080719891

处理插件

image-20210807080905838

把对应的配置转为Properties设置进拦截器,最后添加拦截器进配置文件

image-20210807081117976

拦截器底层使用拦截器链管理

与拦截器处理极其类似

image-20210807081224437

解析开发环境信息

image-20210807081422252

MyBatis惯用套路

image-20210807081536034

把对应配置直接转为Properties,然后实例化对象,直接把Properties丢给对象,让对象自己处理

解析提供的数据库Id

image-20210807081821147

MyBatis默认不支持自动转换sql适应不同数据库,可用为sql指定数据库Id显示切换不同数据库语句

image-20210807082220647

获取数据库Id最终使用连接的元数据信息获取的

解析Mapper

image-20210807082343563

先根据package选择,再根据resource,url,class选择且必须唯一(没有默认优先级)

具体解析Mapper文件

image-20210807082739709

image-20210807082829878

解析Mapper的各种节点

解析二级缓存标签

image-20210807083100922

可见对标签有大量的默认值定义

image-20210807083158801

builderAssistant负责根据信息创建缓存对象

image-20210807083546404

newCacheDecoratorInstance体现了缓存的装饰器模式

image-20210807083745275

判断缓存是否有对应属性的set方法有转换类型进行调用

解析命名空间引用缓存

image-20210807084006866

根据命名空间共用缓存

解析ResultMap

image-20210807084811800

解析单列属性

image-20210807085057221

image-20210807085140403

使用建造者模式对根据属性建造对象

对映射的继承进行分析

image-20210807085520502

解析映射构造器

image-20210807085729737

鉴别器的处理

image-20210807085926811

涉及到对嵌套结果映射的处理

解析sql片段

image-20210807090253217

一条映射信息

image-20210807101239947

其中SqlSource表示一条sql语句信息

映射通过XMLStatementBuilder#parseStatementNode进行解析

image-20210807101612683

处理include节点

image-20210807102004153

先找对应的sql片段,在获取内部属性用于填充sql片段参数,最后替换sql标签,其中包含递归解析sql中可能还有include

对应selectKey依旧是先获取信息再使用建造者

image-20210807102616087

selectKey用于自定义查询数据库获取当前主键值,sql类型只能是select

对应sql语句的解析根据是否动态创建不同的

image-20210807102924352

parseDynamicTags

image-20210807103359000

边解析,边判断是否为动态sql,若文本对象存在${}或有节点对象都是动态sql

image-20210807104208066

通过nodeHandlerMap获取对应的标签处理器,若获取为空会直接报错

image-20210807103731699

image-20210807103751022

DynamicCheckerTokenParser为对应的TextHandler但是不处理任何文本替换逻辑,只是记录是否发生过${}的文本替换

对应不同的标签处理都是先获取信息创建对应的节点对象加入集合

image-20210807104323039

这是foreach标签处理过程

处理命名空间

image-20210807104753677

会把命名空间加入配置,防止重复解析,并添加Mapper接口映射

添加接口Mapper时会构建MapperAnnotationBuilder使用parse处理接口上的注解

image-20210807104953041

对注解的解析与xml类似不过没有共用

重新解析失败的对象集合

image-20210807105735629

xml解析是从上往下的,上面的对象可能引用下面的对象造成解析失败暂存起来,最后再次解析

image-20210807105958366

挨个重新解析,解析一个移除一个,若再次出错,直接忽略

Mybatis使用OgnlCache对Ongl封装使其支持缓存

image-20210807110412633

只是简单的Map缓存

DynamicContext负责拼接sql语句

image-20210807110719245

image-20210807110833315

ContextMap继承HashMap并对get进行重写

image-20210807110930674

增加了更多的取值途径(类似Stuts2包装的request)

SqlNode负责调用DynamicContext的拼接方法

image-20210807111317882

image-20210807111413556

例如if的apply方法,返回布尔值表示是否解析该标签内部标签

SqlSourceBuilder处理SqlNode拼接完毕的sql

image-20210807112822230

主要处理占位符(GenericTokenParser),最终都是要处理为静态sql(StaticSqlSource)

文本处理器为ParameterMappingTokenHandler

image-20210807113128865

对于#{}直接获取对应的参数,并替换为?

image-20210807113304840

image-20210807113438455

处理#{name,javaType=string}等占位符信息

BoundSql主要记录sql及其参数信息

image-20210807113625663

DynamicSqlSource#getBoundSql

image-20210807113826780

会调用sqlNode的apply方法拼接sql

RawSqlSource

image-20210807114147640

RawSqlSource是只包含#{}或普通的sql语句,直接对#{}替换为?并保留对应的参数信息即可

ResultSetHandler的唯一实现类DefaultResultSetHandler

image-20210807114543411

ResultSetHandler用于处理返回的结果集,DefaultResultSetHandler根据xml配置处理返回结果集

image-20210807115247846

遍历处理每行结果

image-20210807142915597

处理单行数据按有没有父mapping或是否用户提供了resultHandler来处理

image-20210807143345301

有嵌套结果集的处理

image-20210807143456399

简单结果集的处理

image-20210807143528109

默认对应数据偏移的处理

image-20210807143908204

根据是否需要停止与查询条数判断是否需要停止

image-20210807144500302

获取对应的ResultMap

image-20210807144711471

根据结果映射封装结果

image-20210807162203383

根据数据创建对象,若有嵌套查询且为懒加载使用代理对象

image-20210807162446939

创建对象,分4中情况

1,结果集只有1列且有对应的TypeHandler转换类型为对应的Java类型

2,有构造器信息使用构造器

3,有默认构造器,使用objectFactory用默认构造器创建对象

4,根据构造器签名寻找合适的构造器

image-20210807162950118

获取对应的构造器参数,使用构造器创建

根据构造器签名获取对应的构造器

image-20210807163537096

先获取所有构造器依次判断

image-20210807163731447

获取对应的构造器参数

用于存储处理每一行的结果

image-20210807142311976

image-20210807142614125

记录映射过与没有映射过的列名

自动映射行为

image-20210807163228361

先判断ResultMap是否配置,否则根据是否嵌套映射与全局配置做判断

对没有映射的列进行自动映射(有缓存机制)

image-20210807164302941

image-20210807164727170

获取数据结果3种情况:1,嵌套查询结果 2,多结果集处理 3,普通结果集的处理

存储结果

image-20210807165100756

image-20210807165221114

若存在父映射,放入父映射,否则直接放入resultContext

处理集合属性

image-20210807173111663

若没有获取到Java类型会通过属性对应的Setter方法获取,判断是否是集合,若是由objectFactory创建处理

延迟加载主要看fetchType或配置文件的全局配置

因为JavaBean大多数没有实现任何接口,懒加载使用CGLIB或Javassist进行代理

ResultLoader用于保存一次延迟加载所需的信息

image-20210807173936750

image-20210807174141316

延迟加载主方法

ResultLoaderMap存储懒加载的属性

image-20210807174449999

load加载指定属性,loadAll加载所有属性

image-20210807174728998

pair.load();最终创建ResultLoader加载结果

代理对应的拦截器(这时CGLIB的,Javassist与此类似)

image-20210807175043828

lazyLoader用于存储懒加载属性,aggressive触发属性加载时是全部加载还是只加载部分属性(全局配置文件中配置)

lazyLoadTriggerMethods触发延时加载的方法列表

Jdbc3KeyGenerator用于自动生成主键

image-20210807185751614

在数据库操作后进行处理

image-20210807190003904

把返回的结果(id)封装,最终还要给对象赋值(例如INSERT)

SelectKeyGenerator用于sql语句生成主键

image-20210807190652016

在处理前或后执行sql语句

image-20210807190954932

执行sql语句获取主键

StatementHandler串联处理过程的类

image-20210807192657437

prepare获取对应的Statement对象,parameterize进行参数填充

RoutingStatementHandler

image-20210807191246102

自己不实现功能,根据传入类型创建对应的实现,功能实现都靠创建的实现类

BaseStatementHandler对sql执行的一些公共数据进行定义

image-20210807191600540

image-20210807192341499

执行查询

image-20210807192359398

执行,增删改等

DefaultParameterHandler对PreparedStatement设置参数

image-20210807192003535

image-20210807192149623

StatementHandler初始化参数就是调用ParameterHandler

PreparedStatementHandler

image-20210807192605050

获取PreparedStatement

Mybatis有哪些Executor执行器

SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

ReuseExecutor:使用先查询map,没有创建,使用完毕放回map 实现复用

BatchExecutor:将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理

设置:可以在配置文件的settings标签设置(默认设置),也可以在openSession(枚举)设置(覆盖设置)

image-20210807193217389

BaseExecutor实现了大部分方法,关键部分留下模板方法让子类实现

image-20210807193847590

查询时会使用缓存

image-20210807193922346

增删改时会清除一级缓存

image-20210807194113107

查询时会先在缓存中放占位符,查找完毕再设置具体值

image-20210807194450534

提交方法,会清除缓存,执行缓存的sql语句,必要的话提交事务

ReuseExecutor会通过sql与Statement管理对Statement复用

image-20210807194719973

BatchExecutor每个Statement都包含多个sql语句

image-20210807195210716

image-20210807195340847

相同的sql放到同一个Statement里

DefaultSqlSessionFactory

image-20210807200809983

根据数据库配置文件获取

image-20210807200959969

根据传入数据库连接获取

就这两种方式

SqlSessionManager

image-20210807201355601

image-20210807201522045

既可以用于获取SqlSession,也可直接当SqlSession操作

获取SqlSession源于传入的SqlSessionFactory,SqlSession操作源于sqlSessionProxy拦截器中实际使用每个线程绑定的SqlSession

简述Mybatis的插件运行原理,以及如何编写一个插件

原理对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口事件进行拦截

实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,最后在配置文件中配置编写的插件

image-20210807204509596

注解解释:可以使用多个Signature拦截多个方法,type拦截那个对象,method拦截那个方法,args用于区分方法重载

image-20210807204718179

根据配置创建执行器

image-20210807202356805

通过CachingExecutor实现二级缓存(装饰器)

image-20210807202621503

image-20210807202645687

通过pluginAll使用插件对执行器层层代理

image-20210807202825739

在拦截器中拦截方法的执行实现插件

与Spring对接的ClassPathMapperScanner

image-20210807205617785

主要是把符合条件接口的BeanDefinition设置类信息为MapperFactoryBean

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值