mybatis框架知识

一、Mybatis高级应用

映射文件:约束头;根标签;命名空间;操作标签(curd);结果实体类;语句的id标识;sql语句

Environment标签:数据库配置环节

TransactionManager标签:事务管理器

DataSource标签:数据源->pooled 使用连接池管理,unpooled反之

Mapper标签:加载映射,可单独加也可以直接加一个包下所有。

Propreties标签:加载外部对应文件,比如提取jdbc或者日志配置文件

TypeAliases标签:给实体类全限定名起别名,使用packge可以批量

forEach标签:collection遍历的集合;open开始部分;close结束部分;item遍历的元素;sperator分隔符

代理模式开发接口规范:

\1. mapper.xml的namespace与接口限定名一致

\2. mapper接口方法名与mapper.xml定义的每个statament的id相同

\3. mapper接口方法的入参对应mapper.xml的parameterType类型相同

\4. mapper接口方法的返回结果对应mapper.xml的resultType类型相同

Mybatis复杂映射:通过resultMap手动配置实体属性与表配置关系。一对一:在resultMap标签内通过assocition标签配置实体类引用的另一个实体类字段。一对多、多对多:collection标签。

Mybatis注解开发;使用@result注解代替resultMap,column数据库列名,property装配的属性名,@one一对一,@many一对多,多对多。(通过当前方法上面的查询结果根据@result注解内关联字段,调取另一个方法注解关联sql,获取综合返回结果)

Mybatis一级缓存;存储在sqlsession中,默认开启,底层是一个hashmap(cachekey键(statsmentid+params+boundsql+rowbounds),值(具体对象))。第一次查询先去缓存中查找,找到直接返回,没找到去数据库获取,返回查询结果并缓存。事务提交操作会清空缓存。

一级缓存源码理解:根据父子关系翻阅源码,比如sqlsession(clearCache)->defaultSqlsession(clearCache)->excutor(clearLocalCache)->

baseExcutor(clearLocalCache)->perpetualCache(clear)查看最底层,他定义的就是一个hashmap,得出结果缓存就是存放map对象。方法执行底层中都要调用createCacheKey获取cachekey,在方法内通过Configuration.getEnvironment是否为空来决定是否完成cachekey值的封装。根据key是否为空来判定缓存的添加。

Mybatis二级缓存

横跨sqlsesion,默认关闭,通过注解标签@CacheNamespace开启二级缓存,多个sqlsession共享二级缓存,二级缓存的不是对象,是缓存的数据,注意二级缓存实体类要实现序列化接口,因为二级缓存能存储内存和硬盘。可以引入外部redis非关系性数据库来进行二级缓存,自定义缓存类必须要实现cache包下的Cache接口。

全局二级缓存开启

Mybatis整合Redis缓存:解决分布式二级缓存,对缓存数据进行集中管理,从redis缓存服务器中获取缓存数据,redis源码自带默认配置。缓存的数据也是一个hash结构。

三层架构:api接口层->传统或者代理方式、数据处理层->数据库操作、框架支撑层->连接,事务,配置,缓存等等。

Sqlsession:作为mybatis工作的主要顶层api,表示和数据库交互的会话,完成数据库curd功能

Excutor:Mybatis执行器,负责sql语句的生成和查询缓存的维护

BatchExecutor (重⽤语句并执⾏批量更新)

ReuseExecutor (重⽤预处理语句 prepared statements)

SimpleExecutor (普通的执⾏器,默认)

(1、根据传递的参数,完成SQL语句的动态解析,⽣成BoundSql对象,供StatementHandler使⽤;

(2、为查询创建缓存,以提⾼性能

(3、创建JDBC的Statement连接对象,传递给StatementHandler对象,返回List查询结果。

StatementHandler:封装了jdbc statement操作,设置参数,将结果转化成list集合

对于JDBC的PreparedStatement类型的对象,创建的过程中,我们使⽤的是SQL语句字符串会包含若⼲个?占位符,我们其后再对占位符进⾏设值。StatementHandler通过parameterize(statement)⽅法对 S tatement 进⾏设值;

StatementHandler 通过 List query(Statement statement, ResultHandler resultHandler)⽅法来完成执⾏Statement,和将Statement对象返回的resultSet封装成List;

ParameterHandler:把传递的参数转化成jdbc statement所需要的参数

ResultSetHandler:将jdbc返回的结果集转化成list类型集合

TypeHandler:负责java数据和jdbc数据类型之间的映射和转换

MappedStatement:维护了一条<select | update | delete | insert>节点的封装

SqlSource:根据用户传递的parameterObject动态生成sql语句,封装到BoundSql对象并返回

BoundSql:动态生成sql语句和参数信息

Mybatis执行总体流程:

\1. 加载配置文件并初始化

\2. 接受调用请求

\3. 处理操作请求(根据sql的id查找对应的mappedstatement对象->根据传入参数解析mappedstatement对象,得到sql和入参->获取数据库连接并执行sql,返回结果->根据mappedstatement对象结果映射配置对执行结果进行转化处理,得到最终返回结果)

\4. 返回处理结果

Mybatis二级缓存解析

⼆级缓存构建在⼀级缓存之上,在收到查询请求时,MyBatis ⾸先会查询⼆级缓存,若⼆级缓存未命中,再去查询⼀级缓存,⼀级缓存没有,再查询数据库。二级缓存->一级缓存->数据库与⼀级缓存不同,⼆级缓存和具体的命名空间绑定,⼀个Mapper中有⼀个Cache,相同Mapper中的MappedStatement共⽤⼀个Cache,⼀级缓存则是和 SqlSession 绑定。

Cache标签解析:在xml配置文件初始化的时候,在build方法内通过XmlConfigBuilder类的parser方法对configuration标签下所有子节点标签进行解析,找到引入的mapper配置标签,进入对应mapper内部解析方法,在resource或url或class任意不为空的情况下,调用构造的XMLMapperBuilder类中parse方法对mapper节点进行解析,在对应逻辑中找到cache处理模块(里面还有一个相关的处理cache-ref标签解析,是应对引入其他缓存数据库作为二级缓存)。获取标签内所有可定义属性,根据解析的属性通过builderAssistant.useNewCache方法来创建cache对象,最后存入configuration,并赋值给MapperBuilderAssistant的currentCache,而这个变量在构造MappedStatement的build对象时会对currentCache封装,所以相同mapper的MappedStatement共用一个caChe

img

img

img

二级缓存的执行:在使用端通过对具体方法的调用,比如查询,到重载方法后会执行executor.query()方法,因为开启了二级缓存,这时候执行器的query方法就会走CachingExecutor类,而不是默认的BaseExecutor类。在对应类里面获取BoundSql对象并创建CacheKey对象,把参数带入query方法,在具体方法内如果cache不为空,进入具体逻辑,首先有一个flushCacheIfRequired方法通过flushCache=”true”设置属性对缓存是否清空进行处理,接着会有一个tcm.getObject方法,从二级缓存中获取结果,如果为空执行delegate.query()方法从一级缓存获取结果,SimpleExecutor其实就是delegate的具体实现。如果没有再从数据库获取,最后通过tcm.setObjetc再把结果存入一个map对象中,这里还没有存入二级缓存,在执行commit或者close时,才会把map对象转移到二级缓存中,而tcm就是TransactionalCacheManager事务缓存管理器(说到事务就明白了撒,进行事务管控,没有提交就暂时把数据操作临时存放,待完成后进行事务提交,完成最终操作) ,但是⼆级缓存是从 MappedStatement 中获取的。由于 MappedStatement 存在于全局配置中,可以多个 CachingExecutor 获取到,这样就会出现线程安全问题。除此之外,若不加以控制,多个事务共⽤⼀个缓存实例,会导致脏读问题。因此设计可一个Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();来映射Cache 与 TransactionalCache的关系。在进行tcm.setObject时, 存储⼆级缓存对象的时候是放到了TransactionalCache.entriesToAddOnCommit这个map中。而查询又是TransactionalCache.delegate中去查询,避免了数据问题的产生,在提交之后,通过flushPendingEntries方法把数据加载进了delegate中,二级缓存数据正式生效。

img

img

img

img

img

img

延迟加载

一对多,多对多采用延迟加载,什么时候用什么时候加载

一对一,多对一采用;立即加载,默认采用策略

优点:延迟加载可以实现先从单表查询,需要时再关联其他表获取数据,提高查询效率,优化数据库性能。

缺点:因为只有当需要⽤到数据时,才会进⾏数据库查询,这样在⼤批量数据查询时,因为查询⼯作也要消耗时间,所以可能造成⽤户等待时间变⻓,造成⽤户体验下降。

在association和collection标签中都有⼀个fetchType属性,通过修改它的值,可以修改局部的加载策略

局部延迟加载

img

全局延迟加载

img

它的原理是,使⽤ CGLIB 或 Javassist( 默认 ) 创建⽬标对象的代理对象。当调⽤代理对象的延迟加载属性getting ⽅法时,进⼊拦截器⽅法。⽐如调⽤ a.getB().getName() ⽅法,进⼊拦截器的invoke(…) ⽅法,到invoke方法,通过代理拦截到指定⽅法,执⾏数据加载Invoke方法:获取或设置实体类属性延迟加载底层判断,第一层判断是否是延迟加载的属性,第二层是否是setting方法,移除延迟加载,第三层是否是getting方法,并且配置了延迟加载, 发现 a.getB() 需要延迟加载时,那么就会单独发送事先保存好的查询关联 B对象的 SQL ,把 B 查询上来,然后调⽤ a.setB(b) ⽅法,于是 a 对象 b 属性就有值了,接着完成 a.getB().getName() ⽅法的调⽤。

Mybatis设计模式

img

构建者设计模式

Builder模式的定义是”将⼀个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。”,它属于创建类模式,⼀般来说,如果⼀个对象的构建⽐较复杂,超出了构造函数所能包含的范围,就可以使⽤⼯⼚模式和Builder模式,相对于⼯⼚模式会产出⼀个完整的产品,Builder应⽤于更加复杂的对象的构建,甚⾄只会构建产品的⼀个部分,直⽩来说,就是使⽤多个简单的对象⼀步⼀步构建成⼀个复杂的对象。

img

工厂模式

在Mybatis中⽐如SqlSessionFactory使⽤的是⼯⼚模式,该⼯⼚没有那么复杂的逻辑,是⼀个简单⼯⼚模式。

简单⼯⼚模式(Simple Factory Pattern):⼜称为静态⼯⼚⽅法(Static Factory Method)模式,它属于创建型模式。

在简单⼯⼚模式中,可以根据参数的不同返回不同类的实例。简单⼯⼚模式专⻔定义⼀个类来负责创建其他类的实例,被创建的实例通常都具有共同的⽗类。

在mybatis中SqlSessionFactory负责SqlSession创建,并且的openSession ()⽅法重载了很多个,分别⽀持autoCommit、Executor、Transaction等参数的输⼊,来构建核⼼的SqlSession对象。

在DefaultSqlSessionFactory的openSessionFromDataSource方法,通过获取的数据库连接、事务对象、执行器,最后构建了defaultSqlSession。

代理模式

代理模式(Proxy Pattern):给某⼀个对象提供⼀个代理,并由代理对象控制对原对象的引⽤。代理模式的英⽂叫做Proxy,它是⼀种对象结构型模式,代理模式分为静态代理和动态代理,我们来介绍动态代理,我们使⽤Configuration的getMapper⽅法时,会调⽤mapperRegistry.getMapper⽅法,⽽该⽅法⼜会调⽤mapperProxyFactory.newInstance(sqlSession)来⽣成⼀个具体的代理,通过T newInstance(SqlSession sqlSession)⽅法会得到⼀个MapperProxy对象,这个MapperProxy对象实现了InvocationHandler重写了invoke方法,该⽅法则会调⽤后续的sqlSession.cud>executor.execute>prepareStatement 等⼀系列⽅法,完成 SQL 的执⾏和返回,最后调⽤T newInstance(MapperProxy mapperProxy)⽣成代理对象然后返回 。

------ 本文结束感谢您的阅读 ------
请我一杯咖啡吧!
itingyu 微信打赏 微信打赏