MyBatis3源码深度解析(十四)SqlSession的创建与执行(一)Configuration与SqlSession的创建过程

本文主要是介绍MyBatis3源码深度解析(十四)SqlSession的创建与执行(一)Configuration与SqlSession的创建过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 第五章 SqlSssion的创建过程
    • 前言
    • 5.1 XPath方法解析XML文件
      • 5.1.1 XPath的基本用法
      • 5.1.2 MyBatis使用XPathParser工具类
    • 5.2 Configuration实例创建过程
    • 5.3 SqlSession实例创建过程

第五章 SqlSssion的创建过程

前言

MyBatis的核心组件之一SqlSession对象,表示框架与数据库建立的会话,通过该对象的实例可以完成对数据库的增删改查操作。

SqlSession对象的创建过程可以拆解为3个阶段:Configuration实例的创建过程、SqlSessionFactory实例的创建过程和SqlSession实例化的过程。

5.1 XPath方法解析XML文件

MyBatis的主配置文件和Mapper配置文件都是使用的是XML格式。

MyBatis框架在启动时,会解析这些XML配置,将配置信息转换并注册到Configuration组件中。

JDK API提供了3种方式解析XML,分别为DOM、SAX和XPath。这3种方式各有特点,MyBatis选用的方式是XPath

因此,在研究Configuration组件的创建过程之前,有必要研究一下如何通过XPath解析XML文件。

5.1.1 XPath的基本用法

首先创建一个XML文件users.xml:

<?xml version="1.0" encoding="UTF-8"?><users><user id="1"><name>孙悟空</name><age>1500</age><phone>18955245635</phone><birthday>0000-01-01</birthday></user><user id="2"><name>猪八戒</name><age>1000</age><phone>14577898652</phone><birthday>0600-10-01</birthday></user>
</users>

该XML文件中定义的用户信息与Java实体类User的属性是一一对应的:

public class User {private Integer id;private String name;private Integer age;private String phone;private Date birthday;// constructor getter setter ...
}

然后编写测试代码:

示例1@Test
public void testXPath() throws IOException, ParserConfigurationException, SAXException, XPathExpressionException, ParseException {// (1)创建表示XML文档的Document对象DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();InputStream inputStream = Resources.getResourceAsStream("users.xml");Document document = builder.parse(inputStream);// (2)创建用于执行XPath表达式的XPath对象XPath xPath = XPathFactory.newInstance().newXPath();// (3)使用XPath对象执行表达式,获取XML内容NodeList nodeList = (NodeList) xPath.evaluate("/users/*", document, XPathConstants.NODESET);List<User> userList = new ArrayList<>();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");for (int i = 1; i < nodeList.getLength() + 1; i++) {String path = "/users/user[" + i + "]";String idStr = (String) xPath.evaluate(path + "/@id", document, XPathConstants.STRING);String name = (String) xPath.evaluate(path + "/name", document, XPathConstants.STRING);String ageStr = (String) xPath.evaluate(path + "/age", document, XPathConstants.STRING);String phone = (String) xPath.evaluate(path + "/phone", document, XPathConstants.STRING);String birthdayStr = (String) xPath.evaluate(path + "/birthday", document, XPathConstants.STRING);User user = new User(Integer.valueOf(idStr), name, Integer.valueOf(ageStr), phone, sdf.parse(birthdayStr));userList.add(user);}userList.forEach(System.out::println);
}

控制台打印执行结果:

User{id=1, name='孙悟空', age=1500, phone='18955245635', birthday=Thu Jan 01 00:00:00 CST 1}
User{id=2, name='猪八戒', age=1000, phone='14577898652', birthday=Sat Oct 01 00:00:00 CST 600}

由 示例1 可知,使用JDK提供的XPath解析XML文件的过程大致如下:

(1)创建表示XML文档的Document对象

无论通过哪种方式解析XML文件,都需要先创建表示XML文档的Document对象。

Document对象的创建依赖于DocumentBuilder对象,而DocumentBuilder对象采用工厂模式创建,因此首先需要调用DocumentBuilderFactory类的newInstance()方法创建一个工厂类,然后调用工厂类的newDocumentBuilder()方法创建DocumentBuilder对象,再调用DocumentBuilder对象的parse()方法创建Document对象。parse()方法需要XML文件的输入流作为参数。

(2)创建用于执行XPath表达式的XPath对象

XPath对象也采用工厂模式创建,因此首先要调用XPathFactory类的newInstance()方法创建一个工厂类,然后调用工厂类的newXPath()方法创建一个XPath对象。

(3)使用XPath对象执行表达式,获取XML内容

XPath表达式是一种在XML文档中用于定位节点的语言。它基于XML的树状结构,允许通过路径表达式来选取节点。XPath表达式可以组合使用,以实现更复杂的查找和选择。

XPath表达式的基本结构如下:
1. 节点选择。使用“/”符号从根节点开始选择,例如“/catalog/cd/price”会选择文档中根节点下的“catalog”子节点下的“cd”子节点下的“price”元素。(简单讲,就是一路往下找)
2. 相对和绝对路径。使用“//”符号选择文档中在任何位置匹配上的节点,例如“//book[@id=‘chinese’]”会选择文档中所有ID为“chinese”的“book”元素。
3. 选取当前节点。使用“.”符号选择当前操作的节点,例如“./childnode”会选择当前节点下的“childnode”子节点。
4. 选取父节点。使用“…”符号选择当前节点的父节点,例如“…/childnode”会选择当前节点的父节点下的“childnode”子节点。
5. 选取属性。使用“@”符号选择节点的属性,例如“@id”会选择元素标签上的ID属性。
6. 选取文本。使用“text()”函数选择节点的文本内容,例如“text()=‘chinese’”会选择所有文本内容为“chinese”的节点。
7. 谓语。放在方括号中的谓语用来筛选节点,例如“//book[price>35]”会选择所有价格大于35的“book”元素。
8. 通配符。使用“”符号选择所有匹配的节点,例如“//[@id]”会选择所有ID属性不为空的节点。
9. 命名空间。使用“@”符号和命名空间前缀来指定节点的命名空间,例如“@xmlns:a=‘http://www.example.com/a’”会选择所有命名空间前缀为“a”的节点。

在 示例1 中,首先执行的XPath表达式是"/users/*",它的执行结果是一个NodeList对象,表示“users”节点下的所有节点,即2个“user”节点,因此for循环中nodeList.getLength()的结果为2。

在for循环中,首先执行的XPath表达式是/users/user[i]/@id,表示获取“user”节点的id属性。然后依次执行的XPath表达式是/users/user[i]/name/users/user[i]/age/users/user[i]/phone/users/user[i]/birthday,分别表示获取“user”节点的“name”、“age”、“phone”、“birthday”元素。

需要注意的是,XPath对象的evaluate()方法的最后一个由XPathConstants类指定的参数,用于设置XPath表达式要返回的值的类型。

源码1javax.xml.xpath.XPathConstantspublic class XPathConstants {private XPathConstants() { }public static final QName NUMBER = new QName("http://www.w3.org/1999/XSL/Transform", "NUMBER");public static final QName STRING = new QName("http://www.w3.org/1999/XSL/Transform", "STRING");public static final QName BOOLEAN = new QName("http://www.w3.org/1999/XSL/Transform", "BOOLEAN");public static final QName NODESET = new QName("http://www.w3.org/1999/XSL/Transform", "NODESET");public static final QName NODE = new QName("http://www.w3.org/1999/XSL/Transform", "NODE");public static final String DOM_OBJECT_MODEL = "http://java.sun.com/jaxp/xpath/dom";
}

由 源码1 可知,XPath表达式的执行结果为XML节点对象(Node、NodeLis等)或者字符串、数值类型、布尔类型等。

5.1.2 MyBatis使用XPathParser工具类

MyBatis为了简化XPath的操作,提供了一个XPathParser工具类封装了对XML的解析操作,同时使用XNode类增强了对XML节点的操作。

因此,使用XPathParser工具类,可以将上面的示例代码修改成如下所示:

示例2@Test
public void testXPathParser() throws IOException {InputStream inputStream = Resources.getResourceAsStream("users.xml");// (1)创建XPathParser工具类XPathParser parser = new XPathParser(inputStream);// (2)调用evalNodes()方法获取所有符合表达式的节List<XNode> nodeList = parser.evalNodes("/users/*");for (XNode node : nodeList) {System.out.println("--------- ");// (3)调用getLongAttribute方法获取节点的属性Long id = node.getLongAttribute("id");System.out.println("id = " + id);List<XNode> children = node.getChildren();for (XNode childNode : children) {// (4)调用getName和getStringBody方法获取节点的名称和值String name = childNode.getName();String stringBody = childNode.getStringBody();System.out.println(name + " = " + stringBody);}}
}

由 示例2 可知,使用XPathParser工具类后,解析XML的逻辑变得更加简便。通过调用XPathParser工具类的evalNodes()方法即可获取所有符合表达式的节点,而获取节点的属性只需调用XNode对象的getLongAttribute()方法,获取节点名称调用getName()方法,获取节点值调用getStringBody()方法。

打开XPathParser工具类的构造方法的源码,可以发现它也是先创建DocumentBuilderFactory对象,再创建DocumentBuilder对象,最后调用DocumentBuilder的parse()方法创建一个Document对象。这与 示例1 的写法是一致的。

5.2 Configuration实例创建过程

Configuration组件是MyBatis非常重要的核心组件之一,主要有以下3个作用:

(1)用于描述MyBatis配置信息,包括主配置文件mybatis-config.xml和Mapper配置文件;
(2)作为容器注册MyBatis的其他组件,例如TypeHandler、MappedStatement等;
(3)提供工厂方法,创建ResultSetHandler、StatementHandler、Executor、ParameterHandler等组件。

在【MyBatis3源码深度解析(十二)MyBatis的核心组件(一)Configuration】中编写的示例项目中,有这样一段代码:

示例3// 读取配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 创建SqlSession
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();

由 示例3 可知,在SqlSession实例化之前,MyBatis会通过SqlSessionFactory的build()方法解析主配置文件及所有Mapper配置文件,创建一个Configuration对象。

源码2org.apache.ibatis.session.SqlSessionFactoryBuilderpublic SqlSessionFactory build(Reader reader) {return build(reader, null, null);
}public SqlSessionFactory build(Reader reader, String environment, Properties properties) {try {// 创建一个XMLConfigBuilder对象XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);// 调用parse()方法创建Configuration实例return build(parser.parse());} // catch finally ...
}public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);
}

由 源码2 可知,在build()方法中会创建一个XMLConfigBuilder对象,该类的parse()方法会返回一个Configuration实例。

源码3org.apache.ibatis.builder.xml.XMLConfigBuilderpublic class XMLConfigBuilder extends BaseBuilder {private final XPathParser parser;public Configuration parse() {// 防止parse()方法被同一个实例调用多次if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;// 调用XPathParser的evalNode()方法创建表示configuration节点的XNode对象// parseConfiguration()方法对XNode对象进行处理parseConfiguration(parser.evalNode("/configuration"));return configuration;}// ......
}

由 源码3 可知,parse()方法首先调用XPathParser工具类的evalNode()方法获取XML配置文件中<configuration>节点对应的XNode对象,接着调用parseConfiguration()方法通过该XNode对象获取更多配置信息。

源码4org.apache.ibatis.builder.xml.XMLConfigBuilderprivate void parseConfiguration(XNode root) {try {// issue #117 read properties first// 处理<properties>标签propertiesElement(root.evalNode("properties"));// 处理<settings>标签Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfsImpl(settings);loadCustomLogImpl(settings);// 处理<typeAliases>标签typeAliasesElement(root.evalNode("typeAliases"));// 处理<plugins>标签pluginsElement(root.evalNode("plugins"));// 处理<objectFactory>标签objectFactoryElement(root.evalNode("objectFactory"));// 处理<objectWrapperFactory>标签objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));// 处理<reflectorFactory>标签reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631// 处理<environments>标签environmentsElement(root.evalNode("environments"));// 处理<databaseIdProvider>标签databaseIdProviderElement(root.evalNode("databaseIdProvider"));// 处理<typeHandlers>标签typeHandlersElement(root.evalNode("typeHandlers"));// 处理<mappers>标签mappersElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}
}

由 源码4 可知,在parseConfiguration()方法中,对于<configuration>标签的子标签,都有一个单独的处理方法,例如propertiesElement()方法处理<properties>标签,settingsAsProperties()方法处理<settings>标签,其它如注释所示。

这些标签具体有什么作用,参考【MyBatis3源码深度解析(十二)MyBatis的核心组件(一)Configuration】。

每个标签的解析细节,可以以处理<mappers>标签为例子来研究。

假设主配置文件mybatis-config.xml中有以下配置:

<mappers><!--方式一:通过指定XML文件的类路径来注册--><mapper resource="mapper/UserMapper.xml"/><!--方式二:通过指定XML文件的完全限定资源定位符来注册--><mapper url="file:///C:\workspace\mybatis_demo2\src\main\resources\mapper\UserMapper.xml"/><!--方式三:通过Mapper接口的类路径来注册--><mapper class="com.star.mybatis.mapper.UserMapper"/><!--方式四:通过Mapper接口所在包路径类注册--><package name="com.star.mybatis.mapper"/>
</mappers>
源码5org.apache.ibatis.builder.xml.XMLConfigBuilderprivate void mappersElement(XNode context) throws Exception {// 传入的参数是<mappers>标签对应的XNode对象if (context == null) {return;}// 遍历<mappers>标签的子标签for (XNode child : context.getChildren()) {if ("package".equals(child.getName())) {// 如果子标签是<package>,则说明要根据包名来扫描(即方式四)// 则将<package>标签的name属性提取出来// addMappers方法会根据包名使用反射机制提取出包下的所有Mapper接口String mapperPackage = child.getStringAttribute("name");configuration.addMappers(mapperPackage);} else {// 如果子标签是<mapper>,则提取<mapper>标签的resource、url、class属性String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");if (resource != null && url == null && mapperClass == null) {// resource属性不为空,url、class属性属性为空 -> 方式一// 使用XMLMapperBuilder加载Mapper配置文件ErrorContext.instance().resource(resource);try (InputStream inputStream = Resources.getResourceAsStream(resource)) {XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,configuration.getSqlFragments());mapperParser.parse();}} else if (resource == null && url != null && mapperClass == null) {// url属性不为空,resource、class属性属性为空 -> 方式二// 使用XMLMapperBuilder加载Mapper配置文件ErrorContext.instance().resource(url);try (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属性不为空,resource、url属性属性为空 -> 方式三// 使用反射机制加载Mapper接口Class<?> mapperInterface = Resources.classForName(mapperClass);configuration.addMapper(mapperInterface);} else {// throw ...}}
}

由 源码5 可知,解析<mappers>标签的mappersElement()针对四种可行的配置方式分别进行了处理,最终将Mapper配置文件或Mapper接口注册到Configuration组件中。其他标签的处理方法的逻辑与这相似,不再赘述。

5.3 SqlSession实例创建过程

由 示例3 可知,SqlSession实例使用工厂模式创建,因此在创建SqlSession实例之前要创建SqlSessionFactory对象。

SqlSessionFactory对象的创建依赖于SqlSessionFactoryBuilder对象,以主配置文件输入流作为参数调用其build()方法。

由 源码2 可知,build()方法中,首先借助XMLConfigBuilder对象对主配置文件进行解析,生成Configuration对象,然后以Configuration对象为参数,调用重载的build()方法。

源码6org.apache.ibatis.session.SqlSessionFactoryBuilderpublic SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);
}

由 源码6 可知,重载的build方法以Configuration对象为参数,创建了一个SqlSessionFactory对象,具体的落地实现类是DefaultSqlSessionFactory。

SqlSessionFactory创建完毕后,调用其openSession()方法即可创建一个SqlSession实例。

源码7org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@Override
public SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,boolean autoCommit) {Transaction tx = null;try {// (1)获取主配置文件中配置的环境信息final Environment environment = configuration.getEnvironment();// (2)创建事务管理器工厂final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);// (3)创建事务管理器tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// (4)根据主配置文件中指定的Executor类型创建对应的Executor实例final Executor executor = configuration.newExecutor(tx, execType);// (5)创建DefaultSqlSession实例return new DefaultSqlSession(configuration, executor, autoCommit);} // catch finally ......
}

由 源码7 可知,openSession()方法主要有5个步骤,首先通过Configuration对象获取主配置文件中通过<environment>标签配置的环境信息,然后根据该标签的<transactionManager>子标签配置的事务管理器类型创建对应的事务管理器工厂,由该工厂创建对应的事务管理器。

事务管理器创建完毕后,调用Configuration对象的newExecutor()方法,根据主配置文件中<settings>标签下的<setting name="defaultExecutorType" value="..."/>配置指定的Executor类型创建对应的Executor实例,默认类型是SIMPLE。

最后以Configuration对象和Executor对象为参数,创建一个DefaultSqlSession对象。至此,SqlSession实例创建过程结束。

本节完,更多内容请查阅分类专栏:MyBatis3源码深度解析

这篇关于MyBatis3源码深度解析(十四)SqlSession的创建与执行(一)Configuration与SqlSession的创建过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/825930

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL