Mybatis主线流程源码解析

   Mybatis的基础使用以及与Spring的相关集成在官方文档都写的非常详细,但无论我们采用xml还是注解方式在使用的过程中经常会出现各种奇怪的问题,需要花费大量的时间解决。

抽空了解一下Mybatis的相关源码还是很有必要。

  先来看一个简单的Demo:

@Test
public void test() throws IOException {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession session  = sqlSessionFactory.openSession();MethodInfo info = (MethodInfo) session.selectOne("com.ycdhz.mybatis.dao.MethodInfoMapper.selectById", 1);System.out.println(info.toString());
}

  这个是官网中入门的一段代码,我根据自己的情况做了一些参数上的改动。这段代码很容易理解,解析一个xml文件,通过SqlSessionFactoryBuilder构建一个SqlSessionFactory实例。

拿到了SqlSessionFactory我们就可以获取SqlSession。SqlSession 包含了面向数据库执行 SQL 命令所需的所有方法,所以我们可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。

代码很简单的展示了Mybatis到底是什么,有什么作用。

 

  Mybatis主线流程:解析Configuration返回SqlSessionFactory;拿到SqlSession对执行器进行初始化 SimpleExecutor;操作数据库;

  

 

  我们先来看一下mybatis-config.xml,在这个xml中包含了Mybatis的核心设置,有获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)等等。
具体的配置信息可以参考官方文档。需要注意的是Xml的属性配置有一定的顺序要求,具体的可以查看http://mybatis.org/dtd/mybatis-3-config.dtd。
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mytest"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><mappers><mapper resource="mybatis/MethodInfoMapper.xml"/><!--<mapper class="com.ycdhz.mybatis.dao.MethodInfoMapper" />--><!--<package name="com.ycdhz.mybatis.dao" />--></mappers>
</configuration>

  test()前两行主要是通过流来读取配置文件,我们直接从new SqlSessionFactoryBuilder().build(inputStream)这段代码开始:

public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);}public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {// SqlSessionFactoryBuilde拿到输入流后,构建了一个XmlConfigBuilder的实例。通过parse()进行解析XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}public SqlSessionFactory build(Configuration config) {//XmlConfigBuilder.parse()解析完后将数据传给DefaultSqlSessionFactoryreturn new DefaultSqlSessionFactory(config);}
}

  

public class XMLConfigBuilder extends BaseBuilder {private boolean parsed;private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration());ErrorContext.instance().resource("SQL Mapper Configuration");this.parsed = false;}public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;
     //这个方法主要就是解析xml文件了parseConfiguration(parser.evalNode("/configuration"));return configuration;}private void parseConfiguration(XNode root) {try {//issue #117 read properties firstpropertiesElement(root.evalNode("properties"));Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);typeAliasesElement(root.evalNode("typeAliases"));pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));typeHandlerElement(root.evalNode("typeHandlers"));mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}} }

   mybatis-config.xml 文件中的mapper属性支持四种配置方式,但是只有package,class这两种发式支持通过注解来配置和映射原生信息(原因在于configuration.addMappers()这个方法)。

private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {String mapperPackage = child.getStringAttribute("name");configuration.addMappers(mapperPackage);} else {String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);InputStream inputStream = Resources.getResourceAsStream(resource);XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());mapperParser.parse();} else if (resource == null && url != null && mapperClass == null) {ErrorContext.instance().resource(url);InputStream inputStream = Resources.getUrlAsStream(url);XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());mapperParser.parse();} else if (resource == null && url == null && mapperClass != null) {Class<?> mapperInterface = Resources.classForName(mapperClass);configuration.addMapper(mapperInterface);} else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}}}}}

   addMappers()调用了MapperAnnotationBuilder.parse()这样一段代码。我们发现当resource不为空的时候,代码首先会调用loadXmlResource()去Resource文件夹下查找(com/ycdhz/mybatis/dao/MethodInfoMapper.xml),

如果发现当前文件就加载。但实际这个时候type信息来自注解,MethodInfoMapper.xml在容器中被加载两次。所以Configruation下的静态类StrictMap.put()时会抛出一个 Mapped Statements collection already contains value for com.ycdhz.mybatis.dao.MethodInfoMapper.selectById 的异常

public class MapperAnnotationBuilder {public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) {loadXmlResource();configuration.addLoadedResource(resource);assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();Method[] methods = type.getMethods();for (Method method : methods) {try {// issue #237if (!method.isBridge()) {parseStatement(method);}} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}private void loadXmlResource() {// Spring may not know the real resource name so we check a flag// to prevent loading again a resource twice// this flag is set at XMLMapperBuilder#bindMapperForNamespaceif (!configuration.isResourceLoaded("namespace:" + type.getName())) {String xmlResource = type.getName().replace('.', '/') + ".xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);} catch (IOException e) {// ignore, resource is not required}if (inputStream != null) {XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());xmlParser.parse();}}}
}

  

  这个时候我们已经完成了xml文件的解析过程,拿到了DefaultSqlSessionFactory。下面我们再来看一下sqlSessionFactory.openSession()的过程:

public class DefaultSqlSessionFactory implements SqlSessionFactory{private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
}public class Configuration {protected boolean cacheEnabled = true;protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;protected final InterceptorChain interceptorChain = new InterceptorChain();public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;if (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {executor = new SimpleExecutor(this, transaction);}if (cacheEnabled) {executor = new CachingExecutor(executor);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;}
}

  

  openSession()方法会调用DefaultSqlSessionFactory.openSessionFromDataSource()方法,在这个方法中会开启事务、创建了执行器Executor:

  MyBatis的事务管理分为两种形式(配置mybatis-config.xml文件的transactionManager属性):

    1)使用JDBC的事务管理机制:即利用java.sql.Connection对象完成对事务的提交(commit())、回滚(rollback())、关闭(close())等

    2)使用MANAGED的事务管理机制:这种机制MyBatis自身不会去实现事务管理,而是让程序的容器如(JBOSS,Weblogic)来实现对事务的管理

     

   执行器ExecutorType分为三类(默认使用的是ExecutorType.SIMPLE):

    1)ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。

    2)ExecutorType.REUSE: 这个执行器类型会复用预处理语句。

    3)ExecutorType.BATCH: 这个执行器会批量执行所有更新语句,如果 SELECT 在它们中间执行还会标定它们是 必须的,来保证一个简单并易于理解的行为。 

        因为Mybatis的一级缓存是默认开启的,查看newExecutor()不难发现,最后通过CachingExecutor对SimpleExecutor进行了装饰(详细代码可以查看https://www.cnblogs.com/jiangyaxiong1990/p/9236764.html)

    

  Mybatis缓存设计成两级结构,分为一级缓存、二级缓存:(参考 https://blog.csdn.net/luanlouis/article/details/41280959 )

    1)一级缓存是Session会话级别的缓存,位于表示一次数据库会话的SqlSession对象之中,又被称之为本地缓存。默认情况下自动开启,用户没有定制它的权利(不过这也不是绝对的,可以通过开发插件对它进行修改);

      实际上MyBatis的一级缓存是使用PerpetualCache来维护的,PerpetualCache实现原理其实很简单,其内部就是通过一个简单的HashMap<k,v> 来实现的,没有其他的任何限制。

    2)二级缓存是Application应用级别的缓存,它的是生命周期很长,跟Application的声明周期一样,也就是说它的作用范围是整个Application应用。

          MyBatis的二级缓存设计得比较灵活,你可以使用MyBatis自己定义的二级缓存实现;你也可以通过实现org.apache.ibatis.cache.Cache接口自定义缓存;也可以使用第三方内存缓存库,如Redis等

  

 

 

    

 

 

  

转载于:https://www.cnblogs.com/jiangyaxiong1990/p/10306981.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/569876.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态规划之背包问题(C语言)

动态规划 动态规划&#xff08;英语&#xff1a;Dynamic programming&#xff0c;简称DP&#xff09;是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题 动态规划思想大致上为&#xff1a;若要解一…

最大子列和问题(JAVA)

最大子列和 问题描述&#xff1a;给定N个整数的序列{A1&#xff0c;A2&#xff0c;A3&#xff0c;…&#xff0c;An}&#xff0c;求解子列和中最大的值。 这里我们给出{-2&#xff0c;11&#xff0c;-4&#xff0c;13&#xff0c;-5&#xff0c;-2}这样一个序列&#xff0c;正…

密码验证

用户在创建一个账户时&#xff0c;需要设置一个密码。密码的复杂程度是安全的保障之一&#xff0c;但是有些用户在设置密码时&#xff0c;总是把密码设置的过于简单&#xff0c;导致用户账户的安全存在威胁。因此&#xff0c;为了提高用户账户的安全性&#xff0c;添加了一个Ja…

阿里云windows/Linux 服务器建站教程,附WordPress配置方法

声明&#xff1a;文章仅供学习使用&#xff0c;故采用了多处链接&#xff0c;如有侵权&#xff0c;请私信我&#xff0c;立删。 最近看到一个学长做的验证界面&#xff0c;很简单的两个文本输入框&#xff0c;但是能给我们群里的小伙伴们做题提供一个验证答案的途径&#xff0…

Maven修改默认仓库为阿里云仓库

Maven 仓库默认在国外&#xff0c; 国内使用难免很慢&#xff0c;我们可以更换为阿里云的仓库。 第一步:修改 maven 根目录下的 conf 文件夹中的 setting.xml 文件&#xff0c;在 mirrors 节点上&#xff0c;添加内容如下&#xff1a; <mirrors><mirror><id>…

apache启动错误 AH00072: make_sock: could not bind to address [::]:443

一、安装apche遇到问题 在电脑上win7系统中安装了wnmp&#xff08;nginxMySQLphp7.2&#xff09;后&#xff0c;想要在安装一套wamp&#xff08;apacheMySQLphp7.2&#xff09;。说做就做&#xff0c;wamp的安装就比较简单了&#xff1a;首先&#xff0c;直接下载apache的压缩包…

Page9:结构分解以及系统内部稳定和BIBO稳定概念及其性质[Linear System Theory]

内容包含系统能控性结构分解、系统能观测性结构分解以及系统结构规范分解原理&#xff0c;线性系统的内部稳定、BIBO稳定概念及其性质 转载于:https://www.cnblogs.com/ERFishing/p/10314720.html

Java集合类及常用数据结构

下面这个图示为JAVA的集合类关系图&#xff0c;没用用严格的UML&#xff0c;了解其中的关系即可&#xff0c;其中颜色强调的几个类为常用的集合类。

win7添加开机启动项

添加开机启动项 开始菜单->所有程序->有个“启动”目录&#xff0c;然后右击选择“打开”&#xff0c;之后把软件的快捷方式移动到此目录下&#xff0c;然后重启电脑&#xff0c;就可以了。 注意&#xff1a;如果修改了程序的安装目录&#xff0c;这里需要删除之前的快捷…

续PA协商过程

续PA协商过程 当sw3的接口恢复之后会发生2中情况。 ①sw3的G0/0/2口先发BPDU ②sw3的G0/0/3口先发BPDU sw3先发送BPDU sw3和sw1的交互过程&#xff1a; sw3的2口恢复后&#xff0c;sw3的所有接口&#xff08;除了边缘端口&#xff09;&#xff0c;变为DP&#xff08;discardi…

搜索算法(一)--DFS/BFS求解拯救同伴问题(JAVA)

拯救同伴问题 问题描述&#xff1a;假设有如下迷宫&#xff0c;求解从某一点出发到目标位置的最短距离 Input: 5 4 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 1 1 4 3 Output: 7 深度优先搜索&#xff08;DFS&#xff09; import java.util.Scanner;public class DF…

PHP7.2的安装与配置(win7)

1、PHP的安装 1&#xff09;、环境安装目录&#xff1a; C:/wamp/|——php|——php7.2|——Apache24|——mysql|——www2&#xff09;、下载 linux专用&#xff1a;http://www.php.net/downloads.php windows专用&#xff1a;http://windows.php.net/download/ 官网里Windo…

搜索算法(二)--DFS/BFS求解炸弹人问题(JAVA )

炸弹人问题 问题描述&#xff1a;小人可以在迷宫中任意地方放置一个炸弹&#xff0c;炸弹可以在以该点为中心的十字方向杀死怪物&#xff0c;但是触碰到墙之后不再能传递攻击。求将一个炸弹放在哪个位置可以杀死更多的怪物&#xff1f;&#xff1f; Input&#xff1a; 13 1…

could not find driver和PDO drivers = no value

could not find driver 使用ThinkPHP5.0.20&#xff08;win7apache2.4.41 php7.2.21MySQL5.7&#xff09;连接MySQL数据库时&#xff0c;报错&#xff1a; 然后使用phpinfo();查看了一下扩展&#xff0c;发现了问题&#xff08;PDO drivers 的值为 no value&#xff09;&…

搜索算法(三)--DFS/BFS求解宝岛探险问题(JAVA )

宝岛探险问题 问题描述&#xff1a;某片海域有诸多岛屿&#xff0c;用0表示海洋&#xff0c;1-9表示陆地&#xff0c;现给定一个岛屿上的坐标点&#xff0c;求解所在岛屿的面积 思路&#xff1a;显然这是一个搜索算法&#xff0c;即只要从当前坐标点开始遍历&#xff0c;每遍…

win7上修改MySQL数据库密码

一、通过命令行方式修改MySQL密码 1、方法一&#xff1a;用mysqladmin 格式&#xff1a;mysqladmin -u用户名 -p旧密码 password 新密码 实例&#xff1a;mysqladmin -uroot -pabc password 123456 2、方法二&#xff1a;用set password 首先&#xff0c;登录MySQL数据库。…

Python爬虫从入门到放弃(二十)之 Scrapy分布式原理

原文地址https://www.cnblogs.com/zhaof/p/7306374.html 关于Scrapy工作流程回顾 Scrapy单机架构 上图的架构其实就是一种单机架构&#xff0c;只在本机维护一个爬取队列&#xff0c;Scheduler进行调度&#xff0c;而要实现多态服务器共同爬取数据关键就是共享爬取队列。 这里重…

python-入门

Python入门 阅读目录 一 编程与编程语言二 编程语言分类三 主流编程语言介绍四 python介绍五 安装python解释器六 第一个python程序七 变量八 用户与程序交互九 基本数据类型十 格式化输出十一 基本运算符十二 流程控制之if...else十三 流程控制之while循环十四 流程控制之for循…

图论算法(一)--最短路径的DFS/BFS解法(JAVA )

最短路径–城市路径问题&#xff1a; 问题描述&#xff1a;求从1号城市到5号城市的最短路径长度 Input&#xff1a; 5 8 1 2 2 1 5 10 2 3 3 2 5 7 3 1 4 3 4 4 4 5 5 5 3 3 Output&#xff1a; 9 DFS import java.util.Scanner; public class minPath {stati…

win7安装composer

一、下载安装包 二、安装 1、双击安装包进行安装 2、安装选项&#xff0c;点击next 3、选择php.exe的路径 4、选择并next 5、代理 6、准备安装 7、信息 8、安装成功 9、测试是否安装成功 10、安装位置 11、密钥位置 12、添加路径到环境变量 13、配置国内镜…