前言

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源码分析与技术原理相关推荐

  1. mybatis源码分析、底层原理

    mybatis用于和数据库交互层面,我们只需定义好Mapper接口,在业务层需要的地方通过@Autowird引入对应的XxxMapper即可,很方便.为什么@Autowird可以从容器中拿到XxxMa ...

  2. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

  3. MyBatis 源码分析-技术分享

    2019独角兽企业重金招聘Python工程师标准>>> MyBatis 源码分析 MyBatis的主要成员 Configuration MyBatis所有的配置信息都保存在Confi ...

  4. Mybatis源码分析--关联表查询及延迟加载原理(二)

    在上一篇博客Mybatis源码分析--关联表查询及延迟加载(一)中我们简单介绍了Mybatis的延迟加载的编程,接下来我们通过分析源码来分析一下Mybatis延迟加载的实现原理. 其实简单来说Myba ...

  5. MyBatis 源码分析系列文章合集

    1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...

  6. MyBatis 源码分析 - SQL 的执行过程

    本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析.运 ...

  7. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  8. MyBatis 源码分析系列文章导读

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  9. 十年老架构师神级推荐,MyBatis源码分析,再也不用为源码担忧了

    十年老架构师神级推荐,MyBatis源码分析,再也不用为源码担忧了 前言 MyBatis是一个优秀的持久层ORM框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL 本身,而不需要花 ...

最新文章

  1. An internal error occurred during: Launching xxx on WebLogic10.x.
  2. 移动端事件 、zepto移动端事件
  3. dnastar拼接反向互补序列_一起看流畅的python:序列构成的数组
  4. HDOJ 2037 今年暑假不AC 【贪心】
  5. jenkins集成sonar
  6. hdu 1421 搬寝室
  7. 怎么就没发现华为Mate20 pro有这么多神奇功能!这波黑科技盘它!
  8. LINQ 花3个礼拜的时间来弄清楚
  9. 台式计算机怎么设置屏幕常亮,怎么设置电脑屏幕一直亮着
  10. (1)快速了解Redis
  11. excel工作表限制编辑怎么删除
  12. 关于SQL sever中创建表时出现“对象名无效”的情况
  13. echarts重写图例点击事件
  14. 搭建java环境和java学习
  15. 百度CarLife Android车机端黑屏问题
  16. PCB抄板最新方法及步骤
  17. JLH统计法统计飞龙(C++) kkmd66
  18. 11.Moveit 末端坐标系姿态表示形式——四元数
  19. 分享2款设备管理系统源码:Springboot医院设备管理+物联网云监控IOT设备管理
  20. 全球工业互联网平台应用案例分析报告

热门文章

  1. Vue和Servlet搭配使用
  2. 软件测试面试丨关于JMeter的问题,看这篇就够了
  3. 基于人工势场法和果蝇优化算法的路径规划(Matlab代码实现)
  4. 帆软报表自定义提交以及单行提交
  5. [附源码]JAVA毕业设计基于web旅游网站的设计与实现(系统+LW)
  6. 经验分享:有关数学建模的几种方法论
  7. 线性代数中两个向量相乘_加两个向量| Python的线性代数
  8. 听哈佛商学院教授讲:如何建立信任
  9. java多线程:线程同步synchronized(不同步的问题、队列与锁),死锁的产生和解决
  10. 测试用例设计方法 之【等价类划分法】