MyBatis 原理浅析——基本原理
前言
MyBatis 是一个被广泛应用的持久化框架。一个简单的使用示例如下所示,先创建会话工厂,然后从会话工厂中打开会话,通过 class 类型和配置生成 Mapper 接口的代理实现,最后使用 Mapper 进行持久化操作。
本文将从 MyBatis 中的 SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession 和 Mapper 几个方面入手简单分析 MyBatis 的实现原理。在后面的系列文章中会进一步具体分析核心类的细节实现。
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 使用 Builder 模式去生成 SqlSessionFactory,因此只提供了多个 build 方法。这些方法可以接受 XML 配置文件的 Reader 或 InputStream 输入流,也可以传入 environment 指定环境或传入 Properties 作为属性。
在 build 方法的实现中,首先根据传入的输入流、environment 和 Properties 构建 XMLConfigBuilder 对象,然后调用其 parse() 方法解析 XML 文件得到 Configuration 对象,最后创建 SqlSessionFactory 对象并返回。
SqlSessionFactory
SqlSessionFactory 是一个工厂接口,默认实现是 DefaultSqlSessionFactory。SqlSessionFactory 的作用是获取 SqlSession,因此提供了多个 openSession 方法,支持从 DataSource 数据源和一个给定的连接 Connection 中创建 SqlSession。
openSession 方法的底层实现可以分为 5 步:
①从 Configuration 对象中获取环境配置 Environment;
②根据环境配置得到事务工厂 TransactionFactory;
③从事务工厂得到事务 Transaction,Transaction 包装了数据库连接,处理数据库连接的创建、准备、提交、回滚和关闭;
④创建执行器 Executor;
⑤创建 SqlSession,返回 DefaultSqlSession 的实例。
其中从 DataSource 数据源创建 SqlSession 的过程如下所示:
- Transaction tx =null;
- try{
- finalEnvironment environment = configuration.getEnvironment();
- finalTransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
- tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
- finalExecutor executor = configuration.newExecutor(tx, execType);
- returnnewDefaultSqlSession(configuration, executor, autoCommit);
- }catch(Exception e){
- closeTransaction(tx);// may have fetched a connection so lets call close()
- throwExceptionFactory.wrapException("Error opening session. Cause: "+ e, e);
- }finally{
- ErrorContext.instance().reset();
- }
SqlSession
SqlSession 是一个接口,默认实现是 DefaultSqlSession,提供了多种数据库操作方式,如 select、selectOne、selectList、insert、update、delete、commit、rollback 和 getMapper 等方法。getMapper 方法用于获取 Mapper 接口的代理实现。在 MyBatis 中建议使用 Mapper 接口操作数据库。
数据库的增删改查和事务的提交回滚都是通过 Executor 执行的。Executor 有 3 种类型 SIMPLE、REUSE、BATCH,默认使用简易执行器 SIMPLE,REUSE 类型执行器重用预处理语句,BATCH 类型执行器重用预处理语句和批量更新。Executor 对象的创建在 Configuration 类型的 newExecutor 方法中进行。
Executor 在执行过程中,会用到 StatementHandler、ParameterHandler 和 ResultHandler,其中 StatementHandler 封装了 java.sql.Statement 的相关操作,ParameterHandler 封装了 SQL 对参数的处理,ResultHandler 封装了对返回数据集的处理。Executor 的执行过程,就是对这 3 个对象的调度过程。更多分析在后续文章中进行。
Mapper
Mapper 是通过 JDK 动态代理实现的,在 MapperProxyFactory 中创建 MapperProxy 并进行接口代理封装。对 Mapper 接口的调用实际上是由 MapperProxy 实现的。
- @SuppressWarnings("unchecked")
- protected T newInstance(MapperProxy<T> mapperProxy){
- return(T)Proxy.newProxyInstance(mapperInterface.getClassLoader(),newClass[]{ mapperInterface }, mapperProxy);
- }
- public T newInstance(SqlSession sqlSession){
- finalMapperProxy<T> mapperProxy =newMapperProxy<T>(sqlSession, mapperInterface, methodCache);
- return newInstance(mapperProxy);
- }
在 MapperProxy 中,实现了 InvocationHandler 的 invoke 方法。methodCache 是一个 ConcurrentHashMap,其中存储了方法与 MapperMethod 的对应关系。从 methodCache 缓存中获取或创建 MapperMethod 对象,然后调用 MapperMethod 对象的 execute 方法执行数据库操作。
- @Override
- publicObject invoke(Object proxy,Method method,Object[] args)throwsThrowable{
- try{
- if(Object.class.equals(method.getDeclaringClass())){
- return method.invoke(this, args);
- }elseif(isDefaultMethod(method)){
- return invokeDefaultMethod(proxy, method, args);
- }
- }catch(Throwable t){
- throwExceptionUtil.unwrapThrowable(t);
- }
- finalMapperMethod mapperMethod = cachedMapperMethod(method);
- return mapperMethod.execute(sqlSession, args);
- }
- privateMapperMethod cachedMapperMethod(Method method){
- MapperMethod mapperMethod = methodCache.get(method);
- if(mapperMethod ==null){
- mapperMethod =newMapperMethod(mapperInterface, method, sqlSession.getConfiguration());
- methodCache.put(method, mapperMethod);
- }
- return mapperMethod;
- }
创建 MapperMethod 对象时,会在构造函数中初始化 SqlCommand 和MethodSignature。SqlCommand 包含了数据库操作的名称,格式为 “接口名.操作名称”,以及 XML 中配置的操作类型,如 select、update等,把一个 Mapper 接口与 XML中的一个配置结合起来。MethodSignature 是方法的签名,标记了方法的返回值类型,对于使用 RowBounds(offset 和 limit 配置)、ResultHandler(结果处理回调)作为参数的方法记录参数位置并初始化参数处理器。
在 MapperMethod 的 execute 方法中,根据 SqlCommand 中的配置选择 SqlSession 的方法,根据 MethodSignature 的配置处理传入的参数,调用 SqlSession 的方法进行数据库操作,最后根据 MethodSignature 的返回值类型返回操作结果。
每周 3 篇学习笔记或技术总结,面向有一定基础的 Java 程序员,内容涉及 Java 进阶、虚拟机、MySQL、NoSQL、分布式计算、开源框架等多个领域。关注作者或微信公众号 backend-develop 第一时间获取最新内容。
MyBatis 原理浅析——基本原理的更多相关文章
- Dubbo学习(一) Dubbo原理浅析
一.初入Dubbo Dubbo学习文档: http://dubbo.incubator.apache.org/books/dubbo-user-book/ http://dubbo.incubator ...
- 《深入理解mybatis原理3》 Mybatis数据源与连接池
<深入理解mybatis原理> Mybatis数据源与连接池 对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题.本文将通过对MyBatis框架的数据源结构 ...
- HTTP长连接和短连接原理浅析
原文出自:HTTP长连接和短连接原理浅析
- Javascript自执行匿名函数(function() { })()的原理浅析
匿名函数就是没有函数名的函数.这篇文章主要介绍了Javascript自执行匿名函数(function() { })()的原理浅析的相关资料,需要的朋友可以参考下 函数是JavaScript中最灵活的一 ...
- 《深入理解mybatis原理》 MyBatis事务管理机制
MyBatis作为Java语言的数据库框架,对数据库的事务管理是其很重要的一个方面.本文将讲述MyBatis的事务管理的实现机制. 首先介绍MyBatis的事务Transaction的接口设计以及其不 ...
- 《深入理解mybatis原理》 Mybatis初始化机制具体解释
对于不论什么框架而言.在使用前都要进行一系列的初始化,MyBatis也不例外. 本章将通过下面几点具体介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XM ...
- 《深入理解mybatis原理》 MyBatis的架构设计以及实例分析
作者博客:http://blog.csdn.net/u010349169/article/category/2309433 MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简 ...
- 深入理解mybatis原理, Mybatis初始化SqlSessionFactory机制详解(转)
文章转自http://blog.csdn.net/l454822901/article/details/51829785 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章 ...
- [转帖]Git数据存储的原理浅析
Git数据存储的原理浅析 https://segmentfault.com/a/1190000016320008 写作背景 进来在闲暇的时间里在看一些关系P2P网络的拓扑发现的内容,重点关注了Ma ...
随机推荐
- Salesforce Javascript(二) 箭头函数
本篇参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions 我们在 ...
- 08 . Jenkins之SpringCloud微服务+Vue+Docker持续集成
简介 大致流程 /* 1.开发人员每天把代码提交到Gitlab代码仓库 2.jenkins从gitlab中拉取项目源码,编译并打包成war包,然后构建Docker镜像,将镜像上传到Harbor私有仓库 ...
- Windows下安装VScode,并使用,以及中文配置
转载:https://blog.csdn.net/x15011238662/article/details/85094006 首先明确一点,VScode是开发Go应用的基础编辑器,是Microsoft ...
- P 2568 GCD
对于这道题,我们要求的是 \(\displaystyle \sum_{i=1}^{N}\sum_{j = 1} ^{N}\) gcd(i,j)为质数 首先我们很容易想出来怎么打暴力,我们可以对于每个 ...
- JavaScript按钮排他思想
要求: 有一系列按钮,要求每单击其中一个,该按钮改变样式(以背景颜色为例),其他按钮恢复保持默认样式. 实现思路: 获取所有按钮元素 首先先把其他按钮的背景颜色去掉 再单独设置自己的样式 代码实现: ...
- CentOS7.7 系统下 virbr0 虚拟网卡的维护与管理
在 CentOS 7 系统的安装过程中,如果有选择相关虚拟化的的服务安装系统后,启动网卡时会发现有一个以网桥连接的私网地址的 virbr0 网卡,这个是因为在虚拟化中有使用到 libvirtd 服务生 ...
- SQL Server查询优化指南
1.尽量不要使用is null,否则将导致引擎放弃使用索引而进行全表扫描.2.char是固定长度,速度快,但占空间,varchar不固定长度,不占空间,但速度慢.3.能使用数字类型就不要使用字符,查询 ...
- K8S基础
四组基本概念 Pod/Pod控制器 Name/Namespace Label/Label选择器 Service/Ingress Pod Pod是k8s里能够被运行的最小的逻辑单元(原子单元) 1个Po ...
- 多测师_测试理轮_002(v模型和H模型)
为什么要测试?1.软件非正常运行或自身缺陷会引发问题2.代码和文档是人写的,难免会出错3.环境原因影响软件(内存不足,存储,数据库溢出等)4.软件测试活动是保证软件质量的关键之一 什么是测试?软件行业 ...
- c# 误区系列(一)
前言 整理很早以前认为的一些误区,准备整理一个系列.新手可以看下,然后大佬指点一下是否哪些地方错了. 正文 值类型存在栈上,引用类型存在堆上 很多人认为用这句话来解释值类型和栈类型的区别,甚至有些文章 ...