mybatis创建sqlsession会话,解析mapper以及sql查询执行的源码分析流程图
2021-05-27 17:18:23 2 举报
mybatis创建sqlsession会话,解析mapper以及sql查询执行的源码分析流程图
作者其他创作
大纲/内容
Y
执行链拦截器,主要是执行插件方法的
configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT
根据namespace+id获取
BaseStatementHandler的构造方法
构造器中指定数据源信息,事务隔离级别
判断二级缓存是否为空
list != null
从事务工厂构建事务
获取namespace命名空间
mappedStatement.getResultMaps()
Y,遍历deferredLoads延迟队列
SerializedCache
不为空,直接从二级缓存中返回数据
判断结果是否为空,并且结果map是否类型处理器
commit()
build()
interceptor.plugin(target)
resultMap.hasNestedResultMaps()
通过MappedStatement获取sql语句对象
获得 CacheKey 对象
context.getStringAttribute(\"namespace\")
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理
构建脚本解析器
SqlSessionFactoryBuilder
N,缓存不存在,创建 ResultLoader 对象
循环mapper的子节点列表
if (cacheEnabled)
获取结果集
getTransactionFactoryFromEnvironment(environment)
cache != null
构建会话工厂
二级缓存基于整个应用为生命周期的按照每个namepace一个缓存来存贮和维护,默认不开启二级缓存可以跨会话查询。二级缓存是线程不安全的,在多线程场景下会发生脏读现象,比如在UserMapper.xml中有大多数针对user表的操作。但是在一个XXXMapper.xml中,还有针对user单表的操作。这会导致user在两个命名空间下的数据不一致。如果在UserMapper.xml中做了刷新缓存的操作,在XXXMapper.xml中缓存仍然有效,如果有针对user的单表查询,使用缓存的结果可能会不正确二级缓存使用条件:1. 当会话提交或关闭之后才会填充二级缓存2. 必须是在同一个命名空间之下3. 必须是相同的statement 即同一个mapper 接口中的同一个方法4. 必须是相同的SQL语句和参数5. 如果readWrite=true ,实体对像必须实现Serializable 接口 二级缓存清除条件:1. xml中配置的update 不能清空 @CacheNamespace 中的缓存数据2. 只有修改会话提交之后 才会执行清空操作3. 任何一种增删改操作 都会清空整个namespace 中的缓存
一级缓存和二级缓存都是基于PerpetualCache进行存放的,底层数据结构就是一个map结构,mybatis执行的整个流程大致是,一条sql语句过来,先查询二级缓存中是否已经有数据记录了,如果开启了二级缓存并且有数据就直接返回,二级缓存没有就去查询一级缓存,如果一级缓存也没有才会去数据库中查询
child.getName()
不为空
解析sql标签
parsePendingResultMaps()
flushCacheIfRequired(ms)
localOutputParameterCache.getObject(key)
propertyMapping.getNestedQueryId() != null
ms.getConfiguration()
N
package,直接获取name元素并添加到configuration的mapperRegistry里去
ms.getBoundSql(parameterObject)
return resultObject
(XMLConfigBuilder)parseConfiguration(parser.evalNode(\"/configuration\"))
判断类型是否为接口类型
queryStack == 0
executor = new CachingExecutor(executor)
resultMap.getPropertyResultMappings()
嵌套id为空,或者不是懒加载,
list == null
deferredLoad.canLoad()
将数据保存在一级缓存中
this.dataSource = ds; this.level = level; this.closeConnection = closeConnection;
(MapperBuilderAssistant)builderAssistant.addMappedStatement
从根节点开始解析
propertyMapping.getNestedQueryId()
mappedStatements.get(id)
N,加入到延迟队列中
statement对象的生命周期存在于一次sql执行中,SQL语句执行完statement也就被释放了
解析
Y,缓存不为空,如果有必要刷新缓存
queryStack++
存储数据
PerpetualCache
ms.getStatementType() == StatementType.CALLABLE
遍历entriesToAddOnCommit.entrySet()的集合调用put方法存放数据
通过configuration对象创建执行器
new DefaultSqlSessionFactory(config)
对查询结果进行封装返回
executorType
为空
循环遍历拦截器列表,依次执行plugin方法
默认执行器是simple
ExecutorType.BATCH
return currentNamespace + \".\" + base
嵌套id不为空,并且是懒加载,
设置当前命名空间
获取二级缓存,作用于mappedStatement
如果localCacheScope作用域为STATEMENT,则清空一级缓存
Y,如果该属性配置了延迟加载,则将其添加到 `ResultLoader.loaderMap` 中,等待真正使用时再执行嵌套查询并得到结果对象
configuration.getEnvironment()
(BaseExecutor)query()
Cache cache = ms.getCache()
type.isInterface()
resultMapElements(context.evalNodes(\"/mapper/resultMap\"))
作用于单次会话,如WEB一次请求期间,不能用作于某个对像属性,也不能在多个线程间共享,因为它是线程不安全的
实例化resultSetHandler
通过resultHandler处理
从configuration对象里获取环境参数
将map中的数据清空
REUSE
判断当前cache类型是否为PerpetualCache
cache.get(key)
往mapperRegistrymap中添加mapper代理工厂
SynchronizedCache
Y,获取resultMap的property
sqlElement(context.evalNodes(\"/mapper/sql\"))
queryStack--
LoggingCache
没有加载过
new ManagedTransactionFactory()
localCache.removeObject(key)
构建一个MappedStatement对象象并加入到configuration全局对象中
是否懒加载
clearLocalCache()
构建唯一的id值,以namespace+标签id作为key
CALLABLE
ExecutorType.REUSE
buildStatementFromContext(context.evalNodes(\"select|insert|update|delete\"))
获得内嵌查询的参数类型
(XMLMapperBuilder)mapperParser.parse()
propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()
addMapper(mapperClass)
判断是否动态的
从一级缓存中清除这个key
parsePendingCacheRefs()
setDefaultImplementations()
(Executor) interceptorChain.pluginAll(executor)
N,直接加载
获得内嵌查询的参数对象
cache != null && ms.isFlushCacheRequired()
(MapperBuilderAssistant)builderAssistant.useNewCache()
STATEMENT
具体装饰器,默认是LruCache
N,执行链拦截器,主要是执行插件方法的
hasMapper(type)
如果环境参数没有就new一个
propertyMapping.isLazy()
builderAssistant.setCurrentNamespace(namespace)
Class<?> mapperInterface = Resources.classForName(mapperClass)
flushPendingEntries()
解析statement节点
ps.execute()
将xml解析成configuration对象
在当前base缓存类上反射创建一个新的cache类
查询堆栈加一
加入已加载资源
localCache.clear() localOutputParameterCache.clear()
根据key获取数据
判断是否为空
nestedQuery.getBoundSql(nestedQueryParameterObject)
关闭statement对象
ms.isUseCache() && resultHandler == null
获得内嵌查询的编号
parentMapping != null
解析resultMap标签
通过configuration创建代理对象来进行懒加载的查询,代理方式有CglibProxyFactory,JavassistProxyFactory
mybatis的sql语句执行流程,这个以查询为例,都是执行到DefaultSqlSession里的方法,
从环境参数中获取事务工厂对象
collectionProperty != null
执行查询
configurationElement(parser.evalNode(\"/mapper\"))
deferredLoad.load()
build(parser.parse())
configuration.newMetaObject(collectionProperty)targetMetaObject.add(rowValue)
如果url不为空,从url获取流构建一个mapper解析
调用预处理器查询sql
判断executorType类型
创建一个deferredLoad对象
openSession()
从缓存中获取数据
调用执行器查询
解析mapper文件
判断list是否为空
缓存具体组件,默认是PerpetualCache
获取xml配置的resultMap
循环遍历parents列表数据
PerpetualCache.class.equals(cache.getClass())
mapperElement(root.evalNode(\"mappers\"))
通过configuration全局对象获取statement
为空,直接向原来的MetaObject添加数据
缓存底层数据结构就是hashmap
通过MappedStatement获取configuration对象
查询堆栈减一
执行动态SQL脚本解析
parent != null && rowValue != null
ResultSetWrapper rsw = getFirstResultSet(stmt)
(XMLScriptBuilder)builder.parseScriptNode()
configuration.addMapper(mapperInterface)
pendingRelations.get(parentKey)
检查缓存中已存在
ms.getStatementType()
创建一个缓存构建器
根据parentMapping创建一个parentkey
propertyMapping.getProperty()
获取decorators列表里缓存类的构造器
创建一个xmlconfig的解析器
获得 BoundSql 对象
(DefaultResultSetHandler)resultSetHandler.handleResultSets(ps)
将数据保存在事务对象的一个map中,注意在这里并没有把数据存入到到二级缓存中,只有事务提交后才会将数据同步到二级缓存中,这样做是为了防止事务执行过程回滚导致缓存中出现脏数据,导致脏读
找到指定路径下指定类型的类的set集合
循环遍历propertyMappings
解析properties标签
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null
!configuration.isResourceLoaded(resource)
判断是否开启了二级缓存
当第一次请求时,先把计算出来的key缓存放入一个占位符,当有数据时,在进行put真实的数据
判断是否开启二级缓存
cacheElement(context.evalNode(\"cache\"));
创建一个prepareStatement对sql语句进行预编译
嵌套id不为空,进行嵌套查询
getCacheDecoratorConstructor(cacheClass)
生成缓存对应的key
报错
创建一个懒加载map
不为空,通过configuration创建一个MetaObject并添加数据
这里,cache对应了当前的namespace
(CachingExecutor)query()
(DefaultSqlSession)selectList()
处理结果集
nestedQuery.getParameterMap().getType()
configuration.getMappedStatement(nestedQueryId)
BATCH
用于生成会话的工厂,作用于整个应用运行期间,一般不需要构造多个工厂对象
通过configuration创建一个resultSetHandler
clearOnCommit = true; entriesToAddOnCommit.clear()
closeStatement(stmt)
将数据保存在事务对象的一个map中
LanguageDriver langDriver = getLanguageDriver(lang)
localCache.getObject(key) != null && localCache.getObject(key) != EXECUTION_PLACEHOLDER
isDynamic
循环列表添加mapper
已经加载过
cacheRefElement(context.evalNode(\"cache-ref\")); cacheElement(context.evalNode(\"cache\"));parameterMapElement(context.evalNodes(\"/mapper/parameterMap\"))
TransactionalCache
在这里,mybatis运用了装饰器的模式来创建一系列的cache缓存类,通过组合的形式来实现类的装饰效果,在PerpetualCache的基础上进行功能的添加,使用装饰器而不使用继承,避免了类的体系膨胀具体的装饰链大概是:SynchronizedCache -> LoggingCache -> SerializedCache -> LruCache -> PerpetualCache
通过会话工厂创建一个SqlSession会话
(List<Object>) localCache.getObject(key)
通过configuration全局对象创建一个statementHandler处理器,mybatis默认的是PreparedStatementHandler
N,缓存为空,直接调用执行器的查询
为空,则调用执行器查询
绑定命名空间
从一级缓存获取数据
resultLoader.loadResult()
如果没有指定执行器,默认传入SIMPLE执行器
判断是否已经加载该资源
获取解析语言
根据statementType选择对应的handler
PREPARED
String mapperPackage = child.getStringAttribute(\"name\"); configuration.addMappers(mapperPackage)
如果implementation为空设置默认的implementation为PerpetualCache,如果decorators为空,设置默认的decorators为LruCache
将存储过程的out参数信息存入缓存
如果resource不为空,从resource获取流构建一个mapper解析器
判断是否有嵌套查询
.........
通过装饰器实现了一条有执行顺序的装饰链条,最终会从缓存map中获取数据
ResultLoaderMap lazyLoader = new ResultLoaderMap()
判断是否可以加载
(TransactionalCache)tcm.clear(cache)
获得内嵌查询的 MappedStatement 对象
清空延迟队列数据
如果statement类型为存储过程
configuration.getMappedStatement(statement)
SqlSessionFactory
N,为空,从数据库查询
(SimpleExecutor)doQuery()
return list
mapperRegistry.addMappers(packageName)
(XMLStatementBuilder)statementParser.parseStatementNode()
configuration.addLoadedResource(resource)
MappedStatement statement = statementBuilder.build(); configuration.addMappedStatement(statement)
((ResultHandler<Object>) resultHandler).handleResult(resultContext)
创建结果对象
Y,不为空,从缓存中获取存储过程的数据
ExecutorType
在这个方法里解析cache,并进行装饰器组件的装载
N,将mapper代理工厂存入对应的mapper类型的map中
(InterceptorChain)pluginAll(Object target)
bindMapperForNamespace()
获得属性名
parsePendingStatements()
循环decorators列表,调用方法
(StatementHandler) interceptorChain.pluginAll(statementHandler)
......
propertiesElement(root.evalNode(\"properties\"))
Y,缓存已存在 ,通过执行器创建DeferredLoad 对象,并通过该 DeferredLoad 对象从缓存中加载
String resource = child.getStringAttribute(\"resource\"); String url = child.getStringAttribute(\"url\"); String mapperClass = child.getStringAttribute(\"class\");
判断查询堆栈计数是否为0
如果class不为空,则调用class.forName反射获取类
new CacheBuilder(font color=\"#cc0000\
通过ObjectFactory创建一个object对象
根据id(namespace)反射创建一个base 缓存类
cacheConstructor.newInstance(base)
构建statement解析器
deferredLoads.clear()
SIMPLE
LruCache
0 条评论
下一页