Mybatis源码分析与技术原理
前言
Mybatis框架属于ORM框架,全称(Object Relational Mapping)。用于实现面向对象main车工语言里不同类型系统之间的数据之间的转换。我们在开发中Mybatis框架通常作为Java链接MySQL的工具,那么mytais底层是如何查询数据库的?又是如何将数据库对应的类型(varchar)转换为对应的Java基本类型的?下面我们来介绍mybatis的底层原理。
一、Mybatis是什么?
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
二、传统 JDBC 的弊端:
1、jdbc 底层没有用连接池、操作数据库需要频繁的创建和关联链接。消耗很大的资源
2、写原生的 jdbc 代码在 java 中,一旦我们要修改 sql 的话,java 需要整体编译,不利于系统维护
3、使用 PreparedStatement 预编译的话对变量进行设置 123 数字,这样的序号不利于维护
4、返回 result 结果集也需要硬编码。
二、传统Mybatis使用步骤
1.引入包
1.如果不是Maven项目,要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。
2.如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>x.x.x</version>
</dependency>
2.从 XML 中构建 SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。后面会再探讨 XML 配置文件的详细内容,这里先给出一个简单的示例:
<?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="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><mappers><mapper resource="org/mybatis/example/BlogMapper.xml"/></mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper"><select id="selectBlog" resultType="org.apache.ibatis.demo.Blog">SELECT * FROM Blog WHERE id = #{id}</select>
</mapper>
当然,还有很多可以在 XML 文件中配置的选项,上面的示例仅罗列了最关键的部分。 注意 XML 头部的声明,它用来验证 XML 文档的正确性。environment 元素体中包含了事务管理和连接池的配置。mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。
3.从 SqlSessionFactory 中获取 SqlSession
既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:
try (SqlSession session = sqlSessionFactory.openSession()) {Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}
4.最后的测试类
public class MybatisMain {public static void main(String[] args) throws IOException {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession session = sqlSessionFactory.openSession();Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);System.out.println(blog);}
}
三、Mybatis源码编译与下载
1.首先从github上拉去Mybatis源码项目。
https://github.com/mybatis/mybatis-3
https://github.com/mybatis/parent(依赖)
在目标文件夹下,通过git clone的方式拉取代码。
2.将Mybatis源码项目导入idea开发工具。注意导入的是maven项目。
3.解决parent依赖问题:
在构建的过程中会出现找不到pom.xml中依赖的父模块mybatis-parent的问题。我们需要将paren工程克隆到本地目录中:git clone https://github.com/mybatis/parent.git ,然后先进入parent工程下进行mvn clean install 将parent工程依赖的包下载下来、并保证parent工程编译通过,这步不会出现问题,在编译的输出信息中我们会看到parent工程的版本号,如图所示:
pom.xml文件parent依赖的version标签处,如下文。
接下来修改mybatis工程的pom.xml文件中标识parent依赖的地方:
<parent><groupId>org.mybatis</groupId><artifactId>mybatis-parent</artifactId><version>28-SNAPSHOT</version><relativePath>../parent/pom.xml</relativePath></parent>
告诉我们部分插件没有指定的相应的版本号,出于工程的稳定性考虑需要对使用的插件指定其版本号,并给出了合适的版本号,如图红色方框中的文字。我们只要在mybatisg工程的pom.xml文件中找到相对应的插件处添加<version>$NUM</version> 标签即可, $NUM代表具体的版本号。到这我们再执行mvn clean install 指令就可以将mybatis工程构建成功了。
四、Mybatis连接数据库流程
Mybatis连接MySQL数据库,首先Mybatis首先会获取数据源,对数据源执行SQL语句,在执行SQL语句的时候会先获取到连接,创建句柄防止sql注入,访问数据库,最后返回结果集。
1.Mybatis是如何获取数据源
public static void main(String[] args) throws IOException {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession session = sqlSessionFactory.openSession();Blog blog = session.selectOne("org.apache.ibatis.demo.BlogMapper.selectBlog", 101);System.out.println(blog);}
我们的数据源是放在mybatis-config.xml这个配置文件中的,也就是说谁去解析这个配置文件谁就获得了数据源。我们通过debug上述代码的形式去分析。
debug调用链:
private void environmentsElement(XNode context) throws Exception {if (context != null) {if (environment == null) {environment = context.getStringAttribute("default");}for (XNode child : context.getChildren()) {String id = child.getStringAttribute("id");if (isSpecifiedEnvironment(id)) {TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));DataSource dataSource = dsFactory.getDataSource();Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());}}}}
此时我们已经得到了datesource数据源
2.Mybatis是如何执行sql语句
Sql语句主要放在BlogMapper.xml这个配置文件中,也就是说谁去解析BlogMapper.xml文件,谁就获得执行sql的权利。
debug调用链:
private void configurationElement(XNode context) {try {String namespace = context.getStringAttribute("namespace");if (namespace == null || namespace.equals("")) {throw new BuilderException("Mapper's namespace cannot be empty");}builderAssistant.setCurrentNamespace(namespace);cacheRefElement(context.evalNode("cache-ref"));cacheElement(context.evalNode("cache"));parameterMapElement(context.evalNodes("/mapper/parameterMap"));resultMapElements(context.evalNodes("/mapper/resultMap"));sqlElement(context.evalNodes("/mapper/sql"));buildStatementFromContext(context.evalNodes("select|insert|update|delete"));} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);}}
此时通过buildStatementFromContext(context.evalNodes("select|insert|update|delete"));这行代码已经得到了BlogMapper.xml配置文件中的信息。后面就看如何执行sql。
面试题:Mybatis 加载 Mapper配置的方式有几种?
Mybatis官网中说到映射器(mappers)的配置方式。如下:
1.使用相对于类路径的资源引用 resource
<!-- 使用相对于类路径的资源引用 -->
<mappers><mapper resource="org/mybatis/builder/AuthorMapper.xml"/><mapper resource="org/mybatis/builder/BlogMapper.xml"/><mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
2.使用完全限定资源定位符 url
<!-- 使用完全限定资源定位符(URL) -->
<mappers><mapper url="file:///var/mappers/AuthorMapper.xml"/><mapper url="file:///var/mappers/BlogMapper.xml"/><mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
3.使用映射器接口实现类的完全限定类名 class
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers><mapper class="org.mybatis.builder.AuthorMapper"/><mapper class="org.mybatis.builder.BlogMapper"/><mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
4.将包内的映射器接口实现全部注册为映射器 package
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers><package name="org.mybatis.builder"/>
</mappers
这四种加载mapper的方式中哪种优先级最高呢?
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 {// 使用mapper配置的情况String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");if (resource != null && url == null && mapperClass == null) {// resource属性不为空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) {// url属性不为空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属性不为空Class<?> mapperInterface = Resources.classForName(mapperClass);configuration.addMapper(mapperInterface);// resource、url和class只能存在一个} else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}}}}}
3.Mybatis是如何执行数据库
通过上述步骤我们已经可以得到具体的sql语句,只要交给Mybatis的执行器去执行。
Mybatis的执行器有几种?
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
Mybatis中默认使用哪一种Executor执行器?
答:SimpleExecutor执行器。
Mybatis中如何指定使用哪一种Executor执行器?
答:在Mybatis配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数。
总结:
Mybatis源码分析与技术原理相关推荐
- mybatis源码分析、底层原理
mybatis用于和数据库交互层面,我们只需定义好Mapper接口,在业务层需要的地方通过@Autowird引入对应的XxxMapper即可,很方便.为什么@Autowird可以从容器中拿到XxxMa ...
- MyBatis 源码分析 - 缓存原理
1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...
- MyBatis 源码分析-技术分享
2019独角兽企业重金招聘Python工程师标准>>> MyBatis 源码分析 MyBatis的主要成员 Configuration MyBatis所有的配置信息都保存在Confi ...
- Mybatis源码分析--关联表查询及延迟加载原理(二)
在上一篇博客Mybatis源码分析--关联表查询及延迟加载(一)中我们简单介绍了Mybatis的延迟加载的编程,接下来我们通过分析源码来分析一下Mybatis延迟加载的实现原理. 其实简单来说Myba ...
- MyBatis 源码分析系列文章合集
1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...
- MyBatis 源码分析 - SQL 的执行过程
本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析.运 ...
- MyBatis 源码分析 - 映射文件解析过程
1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
- MyBatis 源码分析系列文章导读
1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...
- 十年老架构师神级推荐,MyBatis源码分析,再也不用为源码担忧了
十年老架构师神级推荐,MyBatis源码分析,再也不用为源码担忧了 前言 MyBatis是一个优秀的持久层ORM框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL 本身,而不需要花 ...
最新文章
- An internal error occurred during: Launching xxx on WebLogic10.x.
- 移动端事件 、zepto移动端事件
- dnastar拼接反向互补序列_一起看流畅的python:序列构成的数组
- HDOJ 2037 今年暑假不AC 【贪心】
- jenkins集成sonar
- hdu 1421 搬寝室
- 怎么就没发现华为Mate20 pro有这么多神奇功能!这波黑科技盘它!
- LINQ 花3个礼拜的时间来弄清楚
- 台式计算机怎么设置屏幕常亮,怎么设置电脑屏幕一直亮着
- (1)快速了解Redis
- excel工作表限制编辑怎么删除
- 关于SQL sever中创建表时出现“对象名无效”的情况
- echarts重写图例点击事件
- 搭建java环境和java学习
- 百度CarLife Android车机端黑屏问题
- PCB抄板最新方法及步骤
- JLH统计法统计飞龙(C++) kkmd66
- 11.Moveit 末端坐标系姿态表示形式——四元数
- 分享2款设备管理系统源码:Springboot医院设备管理+物联网云监控IOT设备管理
- 全球工业互联网平台应用案例分析报告