深入理解MyBatis的原理:整个体系

时间:2023-03-09 21:44:21
深入理解MyBatis的原理:整个体系

前言:工作中虽然用到了 MyBatis,可完全不知道为什么,再不学习就晚了,这里将记录我的学习笔记,整个 MyBatis 的体系。

一、简介  

1、传统的JDBC

  JDBC 是一种典型的桥接模式

  使用传统的 JDBC 方式来访问数据库,有一些弊端,工作量相对较大,对处理异常、事务并正确关闭资源十分复杂。

  JDBC 大致有这几步:

    连接数据库(注册驱动和数据库信息);

    操作 Connection,打开Statement对象;

    通过 Statement 执行 SQL,返回结果到ResultSet对象;

    使用 ResultSet 对象读取数据,然后将这些数据转为 POJO 实体类;

    最后需要关闭所有的数据库相关资源。

2、ORM 模型是什么

  对象-关系映射(OBJECT/RELATIONALMAPPING,简称ORM),是随着面向对象的软件开发方法发展而产生的。用来把对象模型表示的对象映射到基于S Q L 的关系模型数据库结构中去。这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的 SQ L 语句打交道,只需简单的操作实体对象的属性和方法 。O R M 技术是在对象和关系之间提供了一条桥梁,前台的对象型数据和数据库中的关系型的数据通过这个桥梁来相互转化 。

  简单地说,ORM 模型就是数据库的表和简单的Java对象(POJO)的映射关系模型。主要解决数据库数据和 POJO 对象的相互映射。 

  有了 ORM 模型,在大部分情况下,程序员只需要了解 java 应用,而无需对数据库相关知识深入了解,就可以写出通俗易懂的程序了。

  ORM 框架的主要功能就是根据映射配置文件,完成数据在对象模型与关系模型之间的映射,同时也屏蔽了一些重复的代码,只暴露简单的 API 供开发人员使用 。

深入理解MyBatis的原理:整个体系

3、MyBatis

  MyBatis是一个实现了 JPA 规范的用来连接数据库并对其进行增删改查操作的开源框架(就和传统的 JDBC 一样,就是个连接数据库的东西),其实,它底层就是一个 JDBC 封装的组件

  我没有用过 Hibernate,但是了解了一下 Hibernate 的缺点,而 MyBatis 就是为了解决 Hibernate 的不足。

  MyBatis 的前身是 Apache 的一个开源项目 iBatis,2010 年这个项目有 apache software foundation 迁移到了 google code,并且改名为 MyBatis。2013 年 11 月迁移到 Github,所以目前 MyBatis 是由 Github 维护的。

4、MyBatis 和 Hibernate 的区别,如何选择

  JDBC:目前极少用到,因为需要提供太多的代码,操作太多的对象,不仅十分麻烦,还极易出错,所以不推荐在项目中直接使用 JDBC。

  Hibernate:是一个标准的ORM框架。入门门槛较高,不需要写sql,sql语句自动生成,对sql语句进行优化、修改比较困难。编程建议,需要我们提供映射的规则,完全可以通过 IDE 生成,同时无需编写 SQL ,所以开发效率确实优于 MyBatis。而且,它也提供了缓存、日志、级联等强大的功能,但是 Hibernate 的缺点也十分明显,多表关联复杂 SQL,数据系统权限控制,根据条件变化的 sql,存储过程等场景使用它十分不方便,而且性能难以通过 sql 优化。所以注定 Hibernate 只适用于不复杂,不太要求性能的项目。应用场景:适用需求变化不多的中小型项目,比如:后台管理系统,erp,orm,oa等。

  MyBatis:专注sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。mybatis是一个不完全的ORM框架,虽然程序员自己写sql,mybatis也可以实现映射(输入映射,输出映射)。它几乎可以代替 JDBC,支持动态列、动态表名,存储过程,同时提供了简易的缓存、日志、级联。Mybatis 具有高灵活度、可优化、易维护等特点。但是它的缺点是需要提供映射规则和 SQL,所以开发工作量要比 Hibernate 大。应用场景:适用需求变化较多的项目,比如:互联网项目。

  在项目中,需要根据实际情况去选择使用哪一个框架。

5、MyBatis 的整体架构分为三层 , 分别是基础支持层 、 核心处理层和接口层

深入理解MyBatis的原理:整个体系

二、基础

1、MyBaits 的核心组件

  • SqlSessionFactoryBuilder (构造器):它会根据配置信息或者代码来生成 SqlSessionFactory (工厂接口)
  • SqlSessionFactory:依靠工厂来生成 SqlSession (会话)
  • SqlSession:是一个既可以发送 SQL 去执行并返回接口,也可以获取 Mapper 的接口。
  • SQL Mapper:它是 MyBatis 新设计的组件,它是有一个 Java 接口和 XML 文件(或注解)构成的,需要给出对应的 SQL 和映射规则。它负责发送 SQL 去执行,并返回结果。

  深入理解MyBatis的原理:整个体系

2、Configuration

  这里我们的 Configuration 的类全限定名为 org.apache.ibatis.session.Configuration,它在 MyBatis 中将以一个 Configuration 类对象的形式存在,而这个对象将存在于整个 MyBatis 应用的生命周期中,以便重复读取和运用。在内存中的数据是计算机系统中读取速度最快的,我们可以解析一次配置的 XML 文件保存到 Configuration 对象中,方便我们从这个对象中读取配置信息,性能高。单例占用空间小,基本不占用存储空间,而且可以反复使用。Configuration 类对象保存着我们配置在 MyBatis 的信息。在 MyBatis 中提供了两个 SqlSessionFactory 的实现类, DefualtSqlSessionFactory 和 SqlSessionManager。不过 SqlSessionManager 目前还没有使用,MyBatis 中目前使用的是 DefualtSqlSessionFactory。

深入理解MyBatis的原理:整个体系

3、映射器

  映射器是由 Java 接口和 XML 文件(或注解)共同组成的,它的作用如下:定义参数类型、描述缓存、描述 SQL 语句、定义查询结果和 POJO 的映射关系。

  一个映射器的实现方式有两种:一种是通过 XML 的方式实现,一种是通过代码+注解实现。第二种方式实现复杂、代码可读性差等,所以建议使用第一种方式。第一种方式其实就是由一个 Java 接口和一个 XML 文件构成(即 MyBatis 代码生成器生成的一样)。

4、映射器,一个没有实现类的接口怎么能运行呢?

  这里需要用到 Java 的动态代理:我们会在 MyBatis 上下文中描述这个接口,而 MyBatis 会为这个接口生成代理类对象,代理对象会根据“接口全路径+方法名”去匹配,找到对应的 XML 文件(或注解)去完成它所需要的任务,返回我们需要的结果。

5、生命周期

  想要保证多线程中 MyBatis 的正确性、高性能、没有并发问题,就必须掌握 MyBatis 中 SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper 的生命周期。

  SqlSessionFactoryBuilder:利用 XML 或者 Java 编码获得资源来构建 SqlSessionFactory 的,通过它可以构建多个 SessionFactory。它的作用就是一个构建起,一旦我们构建了 SqlSessionFactory,它的作用就已经完结了,失去了存在的意义,这是我们就应该毫不犹豫的废弃它,将它回收。所以它的生命周期只存在于方法的局部,它的作用就是生成 SqlSessionFactory 对象。

  SqlSessionFactory:作用是创建 SqlSession,而 SqlSession 就是一个会话,相当于 JDBC 中的 Connection 对象。每次应用程序需要访问数据库,我们就要通过 SqlSessionFactory 创建 SqlSession,所以 SqlSessionFactory 应该在 MyBatis 应用的整个生命周期中。而如果我们多次创建同一个数据库的 SqlSessionFactory,则每次创建 SqlSessionFactory 会打开更多的数据库连接(Connection)资源,那么连接资源就很快会被耗尽。因此 SqlSessionFactory 的责任是唯一的,它的责任就是创建 SqlSession,所以我们果断采用单例模式。如果我们采用多例,那么它对数据库连接的消耗是很大的,不利于我们统一的管理,这样便嗅到了代码的坏味道。

  SqlSession:是一个会话,相当于 JDBC 的一个 Connection 对象,它的生命周期应该是在请求数据处理事务的过程中。它是一个线程不安全的对象,在涉及多线程的时候,我们需要特别的当心,操作数据库需要注意其隔离级别,数据库锁等高级特性。此外,每次创建的 SqlSession 都必须及时关闭它,它长期存在就会使数据库连接池的活动资源减少,对系统性能的影响很大。

  Mapper:是一个接口,而没有任何实现类,它的作用是发送 SQL,然后返回我们需要的结果,或者执行 SQL 从而修改数据库的数据,因此它应该在一个 SqlSession 事务方法之内,是一个方法级别的东西。它就如果 JDBC 中的一个 SQL 语句的执行,它最大的范围和 SqlSession 是相同的

  深入理解MyBatis的原理:整个体系

三、配置

1、全部 MyBatis 的配置元素

  MyBatis 配置 XML 文件的层次结构。这些层次是不能够颠倒顺序的,如果颠倒顺序,MyBatis 在解析 XML 文件的时候就会出现异常了。  

<?xml version="1.0" encoding="UTF-8"?>
<configuration><!--配置-->
<properties/><!--属性-->
<settions/><!--设置-->
<typeAliases/><!--类型命名,不区分大小写-->
<typeHandlers/><!--类型处理器-->
<objectFactory/><!--对象工厂-->
<plugins/><!--插件-->
<environments><!--配置环境-->
<environment>
<transactionManager/><!--事务管理器-->
<dataSource/><!--数据源-->
</environment>
</environments>
<databaseIdProvider/><!--数据库厂商标识-->
<mappers/><!--映射器-->
</configuration>

四、映射器

  MyBatis 是针对映射器构造的 SQL 构建的轻量级框架。

1、select 元素

  有3中传递多个参数的方式:map;@Param;实体。

  这三种方式对比:

  • 使用 Map 传递参数。因为 Map 导致代码的可读性下降,会导致后面的扩展和维护比较困难,所以建议少使用这种方式。
  • 使用 @Param 注解方式。这种方式可读性高,但是当参数个数很多的时候,同样会导致可读性和维护性下降,所以,当参数个数小于等于 5 时,建议采取这种方式。
  • 使用实体 JavaBean 的方式。当参数个数大于 5 时,建议采取这种方式。

2、insert 元素(写demo)

  实现主键回填,即执行完新增后返回主键 id。

3、级联(写demo)

  级联的延迟加载实现原理是通过动态代理来实现的。有一个动态代理对象,里面保存着相关的 SQL 和参数,一旦我们使用这个代理对象的方法,它会进入到动态代理对象的代理方法里,方法里面会通过发送 SQL 和参数,就可以把对应的结果从数据库里查回来,这便是其实现原理。

4、缓存(写demo)

  目前流行的缓存服务器有 MongoDB、Redis、Ehcache 等。无需从磁盘上读入,具有快速读取和使用的特点。如果缓存的命中率高,那么可以极大的提高系统的性能,所以使用缓存的关键在于存储内容访问的命中率。

五、动态 SQL

六、MyBatis 的解析和运行原理

  MyBatis 的运行分为两大部分,第一部分为读取配置文件到 Configuration 对象,用以创建 SqlSessionFactory;第二部分是 SqlSession 的执行过程。

  问题一、Mapper 仅仅是一个接口,而不是一个包含逻辑的实现类,所以 Dao 层是怎么执行的呢?

  答:动态代理。

未完待续。。。

参考书籍《深入浅出MyBatis技术原理与实战》

  链接:https://pan.baidu.com/s/1uPTZZVANU5przcOzT6v_gA 密码:qatn