MyBatis 源码分析-技术分享
2019独角兽企业重金招聘Python工程师标准>>>
MyBatis 源码分析
- MyBatis的主要成员
- Configuration
- MyBatis所有的配置信息都保存在Configuration对象之中,
- 配置文件中的大部分配置都会存储到该类中
- SqlSession
- 作为MyBatis工作的主要顶层API,
- 表示和数据库交互时的会话,完成必要数据库增删改查功能
- Executor
- MyBatis执行器,
- 是MyBatis 调度的核心,
- 负责SQL语句的生成和查询缓存的维护
- StatementHandler
- 封装了JDBC Statement操作,
- 负责对JDBC statement 的操作,
- 如设置参数等
- ParameterHandler
- 负责对用户传递的参数转换成JDBC Statement 所对应的数据类型
- ResultSetHandler
- 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
- TypeHandler
- 负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换
- MappedStatement
- MappedStatement维护一条<select|update|delete|insert>节点的封装
- SqlSource
- 负责根据用户传递的parameterObject,
- 动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
- BoundSql
- 表示动态生成的SQL语句以及相应的参数信息
knownMappers.put(type, new MapperProxyFactory<T>(type));
- sqlSessionFactory.openSession();
openSessionFromDataSource
- Configuration
- spring-mybatis
ClassPathMapperScanner doScan方法的真正调用地方
definition.setBeanClass(this.mapperFactoryBean.getClass()); 注册 beanDefinition class 为mapperFactoryBean
xmlConfigBuilder
configuration = xmlConfigBuilder.getConfiguration();
XMLStatementBuilder
builderAssistant.addMappedStatement...
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(...
XMLMapperBuilder 解析xml 配置文件,包括 mapper.xml 的配置
xmlMapperBuilder.parse();
bindMapperForNamespace 绑定 mapper
configuration.addMapper(boundType);
mapperRegistry.addMapper(type);
knownMappers.put(type, new MapperProxyFactory<T>(type));
return this.sqlSessionFactoryBuilder.build(configuration);
return new DefaultSqlSessionFactory(config);
MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {if (!this.externalSqlSession) {this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);} }
SqlSessionTemplate 本质是 SqlSession 的装饰器
this.sqlSessionProxy = (SqlSession) newProxyInstance( // sqlSessionProxy 本质是 SqlSession的动态代理new Class[] { SqlSession.class },new SqlSessionInterceptor());
if (!isEmpty(this.plugins)) {for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); // 初始化插件if (LOGGER.isDebugEnabled()) {LOGGER.debug("Registered plugin: '" + plugin + "'");}} }
interceptorChain.addInterceptor(interceptor);
protected final InterceptorChain interceptorChain = new InterceptorChain(); // InterceptorChain 是configuration 的成员变量
- 插件的调用是在 创建四大 Handler 的时候 执行 pluginAll
- 是在程序执行阶段
比如:doUpdate 时候,执行 newParameterHandler
StatementHandler handler = configuration.newStatementHandler(
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler; }
- 是在程序执行阶段
processPropertyPlaceHolders
- 执行属性的处理,简单的说,
- 就是把xml中${XXX}中的XXX替换成属性文件中的相应的值
- 执行属性的处理,简单的说,
findCandidateComponents
if (isCandidateComponent(sbd)) {
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); }
- 初始化阶段:
- spring源码学习之整合Mybatis原理分析
- 程序运行阶段:
- 基本运行:
public static void main(String[] args) {//定义 SqlSessionFactorySqlSessionFactory sqlSessionFactory = null;try {//使用配置文件创建 SqlSessionFactorysqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));} catch (IOException ex) {//打印异常.Logger.getLogger(MainCh1.class.getName()).fatal("创建 SqlSessionFactory失败", ex);return;}//定义 sqlSessionSqlSession sqlSession = null;try {//用sqlSessionFactory创建sqlSessionsqlSession = sqlSessionFactory.openSession();//获取MapperUserMapper userMapper = sqlSession.getMapper(UserMapper.class);//执行Mapper接口方法.UserPO user = userMapper.findUser(1);//打印信息System.err.println(user.getUsername());} finally {//使用完后要记得关闭sqlSession资源if (sqlSession != null) {sqlSession.close();}}}
- spring 托管:
- 添加配置
<import resource="spring-mybatis.xml"/>
- 添加配置
- 基本运行:
- 插件:
- 以分页插件为例:
- 需要 继承 Interceptor
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),} )
public class PageInterceptor implements Interceptor {
- 需要 继承 Interceptor
- interceptorChain.pluginAll(executor) 在configuration 内部注册所有的 plugin
- 本质就是getSignatureMap 方法,扫描所有注解,对配置的Signature 方法进行 动态代理
- 代理类就是public class Plugin implements InvocationHandler
- 执行Plugin 的invoke 会判断该方法是否被代理(signatureMap 里面有没有)
- 如果有执行 intercept 方法
- 该方法最后一行执行的proceed 方法,其实就是该方法的invoke 执行
- 以分页插件为例:
- 手写TypeHandler:
- 详见 催收系统CalendarTypeHandler
- 缓存:
- 一级缓存:
- 一级缓存默认开启,SqlSession 级别的
- 验证:
- 相同的查询,连续查询两遍,记录查询用时,会发现第二次快得多
- update、 insert、delete 等语句会触发清除缓存
- 一级缓存,在存在俩sqlsession 时,可能存在脏数据的情况
- 比如,sqlsessionA 两次相同查询t 表中间,
- sqlsessionB 更新了t表数据,
- sqlsessionA 第二次查询的数据就是可能已被修改的脏数据
- 二级缓存:
- 二级缓存默认关闭,SqlSessionFactory 级别的
- 更不靠谱,开启方式:
- 一级缓存:
- N+1问题:
- 存在级联查询(嵌套查询)中
- 外层查询的一条结果数据,由内层查询获得
- 外层查询一次,获得结果数N ,就要进行N 次内层查询
- (官方不鼓励使用,这样产生大量1+N次查询)
- 由于1+N 问题的性能损耗,可以考虑配合使用 延时加载
- 官网解释:
- 存在级联查询(嵌套查询)中
- lazy loading 是怎么做到的?
- 懒加载在级联查询时用到了,SimpleStatementHandler 里面query结果
- DefaultResultSetHandler 处理结果
- handleResultSets -->handleResultSets -->...getRowValue-->createResultObject
- 如果有嵌套查询且开启了懒加载 那么会使用代理工厂来处理(代理工厂类型cglib或javasissit类型(默认))
- 针对某一个属性,当执行
- 新版本,已经变化:
- 解释为什么是“或”的关系:lazyLoadTriggerMethods 包含该方法的时候,
- 说明对象再进行 equals、clone比较,需要所有属性全部查询来才能进行
- protected Set<String> lazyLoadTriggerMethods =
- new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
- protected Set<String> lazyLoadTriggerMethods =
- 说明对象再进行 equals、clone比较,需要所有属性全部查询来才能进行
- 新版本,已经变化:
- 在嵌套查询的时候 get/set 方法会触发 ResultLoaderMap LoadPair load() 方法去查询(我看源代码的理解),
- 我找到了触发函数lazyLoadTriggerMethods 里面没有get/is
- 依赖的是PropertyNamer.isGetter(methodName)
- 懒加载在级联查询时用到了,SimpleStatementHandler 里面query结果
转载于:https://my.oschina.net/u/3847203/blog/3019311
MyBatis 源码分析-技术分享相关推荐
- MyBatis 源码分析系列文章合集
1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...
- 十年老架构师神级推荐,MyBatis源码分析,再也不用为源码担忧了
十年老架构师神级推荐,MyBatis源码分析,再也不用为源码担忧了 前言 MyBatis是一个优秀的持久层ORM框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL 本身,而不需要花 ...
- MyBatis 源码分析 - 缓存原理
1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...
- MyBatis 源码分析 - SQL 的执行过程
本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析.运 ...
- MyBatis 源码分析 - 映射文件解析过程
1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
- MyBatis 源码分析系列文章导读
1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...
- MyBatis 源码分析系列文章导读 1
1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...
- Mybatis源码分析: MapperMethod功能讲解
canmengqian </div><!--end: blogTitle 博客的标题和副标题 --> <div id="navigator"> ...
- mybatis源码分析之事务管理器
2019独角兽企业重金招聘Python工程师标准>>> 上一篇:mybatis源码分析之Configuration 主要分析了构建SqlSessionFactory的过程中配置文件的 ...
最新文章
- leetcode 454 四数相加
- 高级数据结构与算法 | 二叉搜索树(Binary Search Tree)
- C语言试题二十八之编写函数function功能是:从字符中删除指定的字符,同一字母的大、小写按不同字符处理。
- 中小企业的进步:热爱大数据
- 2025年全球5G设备将达到14亿部 但4G仍占主导地位
- Html中 table,list等表格 中 js 的 Checkbox全选,反选,单选,获取数据选中行 的写法
- [Linux: 编程]如何调试GCC段错误-转
- PHP学习笔记 第六讲 PHP数组的创建修改应用
- JAVA多线程编程之异步
- 计算机辅助设计和制造论文,计算机辅助设计与制造CAD-CAM
- linux下ant安装和使用教程,ant安装与简单应用
- Lighting build failed. Swarm failed to kick off UE4光照构建失败
- 行星轨迹制作_用3ds max制作三维行星运动动画
- 玩转视频类信息流广告平台,投放技巧及运营思路看这里
- 入门力扣自学笔记180 C++ (题目编号:886)(涂色问题,可以多看看)
- 学计算机高考英语听力考试,北京:2018年高考英语听力机考问答
- 五子棋-单机游戏-微信小游戏项目开发入门
- 【图像修复】基于matlab损坏图像修复【含Matlab源码 731期】
- 物理木板过河问题java_抖音捡木板过河
- 苹果白屏一直显示苹果_苹果一直白屏怎么办?试试这个办法