![通用源码阅读指导书:MyBatis源码详解](https://wfqqreader-1252317822.image.myqcloud.com/cover/766/33114766/b_33114766.jpg)
第2章 MyBatis概述
在开始一个项目的源码阅读之前,首先要对整个项目有较为全面的了解。需要了解的信息包括项目的产生背景、演进过程、使用方法等,这些信息能够帮助我们直观地勾勒整个项目的外在轮廓。这样,在我们遇到一段代码时就能根据外在轮廓更好地揣测它在整体功能中的作用,最大限度地减少理解偏差。
在本章我们将概括性地了解 MyBatis项目,包括其背景介绍、快速上手方法等,而对于一些更为细节的使用方法,将会在相关部分的源码解析时进行介绍。
2.1 背景介绍
2.1.1 传统数据库连接
数据库是软件项目中存储持久化数据最常用的场所,应用十分广泛。例如,在网站应用中,注册用户的信息、页面展示的信息、用户提交的信息等大都是存储在数据库中的。因此,与数据库进行交互是很多软件项目中非常重要的部分。
然而,软件程序与数据库交互的过程需要建立连接、拼装和执行 SQL语句、转化操作结果等步骤,相对比较烦琐。代码2-1是一个从数据库中查询 User列表的示例。
【代码2-1】
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_26_1.jpg?sign=1739301497-fIgLWbCCF5nhgppxqDgSK41ioWz4VSNu-0-51e4b027e4396d7b0aed34b28d4bf49a)
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_27_1.jpg?sign=1739301497-L0Ki20dVKAOu45cy51slLfZVWBBde2js-0-42b61a733c575f755e2724e4125e67d7)
代码2-1 所示的过程中,第一、二、五步的工作是相对固定的,可以通过封装函数进行统一操作;而第三、四步的操作却因为涉及的输入参数和输出参数的 Java对象不同而很难将其统一起来。
不仅是在数据的查询操作中,在数据的写入、编辑操作中也会面临同样的问题。在进行数据写入和编辑操作时往往需要处理更多的输入参数,需要将这些参数一一拼装到 SQL语句内。
随着 SQL语句及输入参数、输出参数对象的不同,上述代码中第三、四步展示的操作会千变万化,我们只能针对不同对象的不同操作拼装不同的操作语句,然后单独处理返回的结果。数据库写入、读取操作是十分频繁的,这就带来了大量烦琐的工作。
ORM框架就是为解决上述问题而产生的。
2.1.2 ORM框架
在目前主流的软件开发过程中,多使用面向对象的开发方法和基于关系型数据库的持久化方案。
面向对象是在软件工程原则(如聚合、封装)的基础上发展起来的,而关系型数据库则是在数学理论(集合代数等)的基础上发展起来的,两者并不是完全匹配的,它们中间需要信息的转化。例如,在将对象持久化到关系型数据库中时常常需要图2-1所示的转化过程。
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_27_2.jpg?sign=1739301497-VsUMrm2txQcSbnw9Go1aH33fkvTC8KLF-0-11cf5c076326b18f88e6c6d08ea2d659)
图2-1 对象和关系的映射
这样的转化称为对象关系映射(Object Relational Mapping,简称 ORM、O/RM或 O/R mapping)。ORM 会在数据库数据的读取和写入操作过程中频繁发生,为了降低这种转化过程的开发成本,产生了大量的 ORM框架,MyBatis就是其中非常出色的一款。
2.1.3 MyBatis的特点
大多数 ORM框架选择将 Java对象和数据表直接关联起来,用一组对应关系将两者绑定在一起。对象和数据表的映射如图2-2所示。
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_28_1.jpg?sign=1739301497-KERdjF2HcRHXPX1pGHgikEPfHN7hrT9O-0-c0a855cd1d5ea1f3a18ce64eb3ed84f7)
图2-2 对象和数据表的映射
MyBatis则采取了另一种方式,它没有将 Java对象和数据表直接关联起来,而是将 Java方法和 SQL语句关联起来。这使得 MyBatis在简化了 ORM操作的同时,也支持了数据表的关联查询、视图的查询、存储过程的调用等操作。除此之外,MyBatis 还提供了一种映射机制,将 SQL语句的参数或结果与对象关联起来。图2-3形象地展示了 MyBatis的映射机制。
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_28_2.jpg?sign=1739301497-xkBCRWiLAvjK6LfY4SsH3kWTBe4UtoFj-0-f2d5a8264a6855f646e82871a79c25a5)
图2-3 MyBatis的映射机制
这样,使用 MyBatis时,只要调用一个方法就可以执行一条复杂的 SQL语句。在调用方法时可以给方法传递对象作为 SQL语句的参数,而 SQL语句的执行结果也会被映射成对象后返回。因此,关系型数据库被 MyBatis屏蔽了,读写数据库的过程成了一个纯粹的面向对象的过程。
除核心映射功能外,MyBatis 还提供了缓存功能、懒加载功能、主键自增功能、多数据集处理功能等,这些功能的实现原理会在后续的源码阅读中详细介绍。
2.2 快速上手
在这一节中我们将搭建并运行一个包含 MyBatis的项目,从而对 MyBatis的功能建立直观的认识。
MyBatis的使用非常灵活,支持多种配置方式:
· 完全代码配置。MyBatis 提供完整的配置类,用户可以调用这些配置类完成所有配置工作。但是使用这种配置方式会使得配置信息和业务代码混杂在一起,因此很少采用。
· 基于 XML的配置。MyBatis提供使用 XML进行配置的功能。这种配置方式简单明了,经常使用。
· 外部框架配置。因为 Spring、Spring Boot等框架非常流行,MyBatis开发团队也开发了与这些框架进行对接的相关组件。有了这些组件的支持,可以在 Spring、Spring Boot中直接实现 MyBatis的一些简单配置。
下面将基于 Spring Boot来快速配置一个包含 MyBatis的项目。Spring Boot是一个可以快速上手的 Spring 框架,它能够帮助开发者在配置极少的情况下快速建立轻量、易用的Spring 应用。并且 Spring Boot 还提供一个快速生成初始化项目的网站 Spring Initializr:https://start.spring.io/,如图2-4所示。
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_29_1.jpg?sign=1739301497-3DoBtfbgPqCn43qybgUU2psHEAX9Cbc7-0-50a2d84b358cd650342ac935287554a7)
图2-4 Spring Initializr网站
在 Spring Initializr网站中,设置好项目的 Group名称、Artifact名称及项目依赖后,就可以直接将一个初始化好的项目包下载到本地。在快速上手项目中,需要的项目依赖有:
· MyBatis Framework:提供 MyBatis及其与 Spring Boot对接的组件。
· MySQL Driver:提供连接 MySQL数据库的驱动。
· Spring Web:提供基本的 Web访问功能。
之后便可以单击“Generate the project-Ctrl+”按钮,将初始化好的 Spring Boot项目下载到本地。
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_30_2.jpg?sign=1739301497-HuQ6VNIJoY7iMljB5OyYb9NWTRKfloR5-0-41cbe18bb6991a8ba8b5f3ce34e0fc2b)
2.2.1 MyBatis包的引入
观察使用 Spring Initializr初始化的项目,可能会有人疑惑其 POM中没有引用 mybatis包。这是因为引用了 mybatis-spring-boot-starter包,如代码2-2所示。
【代码2-2】
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_30_3.jpg?sign=1739301497-CGD51hIBm8YQC52yf7ZbN3SNd5VUWGi8-0-340edeeecc70f2ad179bc3ee7346d3f5)
打开 mybatis-spring-boot-starter的 POM文件,如代码2-3所示,就可以发现这里不仅引用了 mybatis包,还引用了 mybatis-spring、mybatis-spring-boot-autoconfigure等包。
【代码2-3】
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_30_4.jpg?sign=1739301497-83x29FSAae9VLAozkCSxAdUxEq4TJCIZ-0-6515ec5b015c96ab68c918d632236890)
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_31_1.jpg?sign=1739301497-JyNaMyytpwhklPrBaVUxmLvXGrGDTPY3-0-81b8fa0565909130ea5012bd1dc8d8e0)
mybatis-spring-boot-starter 的 POM 文件中并没有定义 mybatis 包的版本,这是因为mybatis包的版本是在 mybatis-spring-boot-starter的祖父包 mybatis-parent中定义的,如代码2-4所示。
【代码2-4】
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_31_2.jpg?sign=1739301497-kRHijqwbGs05oJVkFPvKWNWDIbDsY125-0-f1ad4fa6a2a1359475355f9a6b92de86)
所以,只要引用了 mybatis-spring-boot-starter包,就间接引用了 mybatis包及其他的一些组件包。
2.2.2 MyBatis的简单配置
首先,在 Spring Boot 的配置文件中设置应用服务端口号,以及要连接的数据库的地址、用户名、密码信息,如代码2-5所示。
【代码2-5】
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_31_3.jpg?sign=1739301497-GRq5MUKGHBycunG480k24gKfKRFH1Ahk-0-c270c29d1161f0452e6c894514bb7e94)
使用了 mybatis-spring-boot-starter包之后,可以省略 MyBatis的配置文件,只需创建映射文件即可。创建的映射文件如代码2-6所示。
【代码2-6】
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_31_4.jpg?sign=1739301497-xivKZPIyOY6RWtfA2Vbblilak4OJ123S-0-1b481d6e5f6a1375c184e12bae713217)
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_32_1.jpg?sign=1739301497-xI0WFM0BshFKtQo1Xf5XXPekCRau6l0T-0-f60c80c64482a1a6f5720eeb7b26b137)
这样,MyBatis的所有配置就完成了。
2.2.3 基于MyBatis的数据库操作
为了让 MyBatis能够完成 ORM的转化工作,首先在示例项目中创建一个模型类,如代码2-7所示。
【代码2-7】
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_32_2.jpg?sign=1739301497-XJyJsM0J7pBsLyysCMPXIGLyFCZ7H5JO-0-d08d63ef8d02506fc01b20eecc666a18)
然后,创建一个与映射文件相对应的映射接口文件,如代码2-8 所示。这里一定不要忘记添加@Mapper注解,正是这个注解声明了该类是一个映射接口。
【代码2-8】
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_32_3.jpg?sign=1739301497-9b6iAZd1RWml0eg7KnihuOdcp1btGYqh-0-491197e58a3812a97e3d1bf11293f46a)
最后,创建一个接收网络请求并通过 MyBatis 进行数据库访问的控制器,如代码2-9所示。
【代码2-9】
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_32_4.jpg?sign=1739301497-Y6DrZGVWTnHV3zSOuf71I6bjgjEMqC3Q-0-27df0cd03adc733d1c7b13278626583b)
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_33_1.jpg?sign=1739301497-UMk78Zes038kgTOrspCPZqgXiX0K7WHl-0-64caa64400c7be5d02ae05cd554881bc)
这样,整个示例项目就搭建完成了。最终的项目文件结构如图2-5所示。
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_33_2.jpg?sign=1739301497-j7zoo5YEpxXHw2Oy8OeKagGVXzEmV1If-0-b08abad9afbc1b91ac965cf0caf8ac8b)
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_33_3.jpg?sign=1739301497-8CrDpXhNSr30NUbRK1IcGc6OLjcJNKtr-0-dfc6903102e9f72f799eea0f2dbd6ead)
图2-5 项目文件结构
这样,所有的设置工作就全部完成了。启动该 Spring Boot项目后,使用浏览器访问该服务的端口,即可在浏览器和控制台看到数据库查询结果,如图2-6所示。
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_33_4.jpg?sign=1739301497-KHpKOhLxjdmcG02SVUEwqlRMXPSxLall-0-c2152888ab216a5505017fc912c39506)
图2-6 数据库查询结果
2.3 MyBatis的核心功能分析
在快速上手示例中,只使用了下面一行代码便完成了数据库的查询操作。在这行代码中,不包含 SQL语句,接收的参数是 Java对象,输出的结果是 Java对象列表。
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_34_1.jpg?sign=1739301497-v3RoePMAK9EcR47fFAXb0ckIR41dZ1Tx-0-72c679d6c047d97e215373205bb0983f)
而 2.1.1节中所介绍的直接使用 JDBC操作数据库的代码如代码2-10所示。
【代码2-10】
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_34_2.jpg?sign=1739301497-SkRLKNqHXmcscTZoXTgjLEXCNrRSPMEp-0-d72d0230b61bce34d18c651055cfdeb8)
可以看出,使用 MyBatis后大大简化了数据库的读写操作。其原因是,在该配置下,MyBatis完成了下面的映射关系:
· 映射文件中的 SQL语句与映射接口中的抽象方法建立了映射。示例中,SQL语句“SELECT*FROM`user`WHERE schoolName=#{schoolName}”对应了“List<User>queryUserBySchoolName(User user)”方法。
· SQL 语句的输入参数与方法输入参数建立了映射。示例中,SQL 语句中的“#{schoolName}”参数对应了方法输入参数 User对象的 schoolName属性。
· SQL语句的输出结果与方法结果建立了映射。示例中,SQL语句的输出结果对应了User对象。
这种映射关系也可以用图2-7表示。
![](https://epubservercos.yuewen.com/1F79ED/17725769906721506/epubprivate/OEBPS/Images/39146_34_3.jpg?sign=1739301497-K4GRzC2R0pOFhts9zdOmTcJGcuE0c8dC-0-03374d996ec6823c995cec59c51c2a00)
图2-7 映射关系
以上映射之间的转化工作是在数据库读写过程中由 MyBatis自动完成的。因此,可以总结出 MyBatis完成的主要工作,这些工作就是 MyBatis的核心功能。
· 将包含 if等标签的复杂数据库操作语句解析为纯粹的 SQL语句。
· 将数据库操作节点和映射接口中的抽象方法进行绑定,在抽象方法被调用时执行数据库操作。
· 将输入参数对象转化为数据库操作语句中的参数。
· 将数据库操作语句的返回结果转化为对象。
在接下来的源码阅读中,把握住 MyBatis的核心功能,并重点关注与核心功能相关的代码,可以使我们在面对纷杂的类和方法时不易迷失。
在其他项目的源码阅读中,也要遵循这样的策略:找出软件项目的核心功能,重点关注与核心功能相关的代码。