目录
一、基础支持层(各个模块的功能)
二、核心处理层
三、接口层
四、ORM的来历
五、常见的持久化框架
1、Hibernate
2、JPA(Java Persistence API)
3、Spring JDBC
4、MyBatis
5、总结
(1)性能角度
(2)可移植性
(3)开发效率
MyBatis 的整体架构分为三层, 分别是基础支持层、核心处理层和接口层:
反射模块 | 该模块对Java 原生的反射进行了良好的封装,提供了更加简洁易用的API ,方便上层使调用,并且对反射操作进行了一系列优化,例如缓存了类的元数据,提高了反射操作的性能。 |
类型转换模块 | 别名机制, 该机制是类型转换模块的主要功能之一。 实现JDBC 类型与Java 类型之间的转换,该功能在为SQL 语句绑定实参以及映射查询结果集时都会涉及。 |
日志模块 | 提供详细的日志输出信息; 能够集成多种日志框架,其日志模块的一个主要功能就是集成第三方日志框架。 |
资源加载模块 | 对类加载器进行封装,确定类加载器的使用顺序,并提供了加载类文件以及其他资源文件的功能。 |
解析器模块 | 对XPath 进行封装,为MyBatis 初始化时解析mybatis-config.xml 配置文件以及映射配置文件提供支持; 为处理动态SQL 语句中的占位符提供支持。 |
数据源模块 | 身提供了相应的数据源实现; 提供了与第三方数据源集成的接口. |
事务管理 | MyBatis 对数据库中的事务进行了抽象,其自身提供了相应的事务接口和简单实现。 |
缓存模块 | My Batis 中提供了一级缓存和二级缓存,而这两级缓存都是依赖于基础支持层中的缓存模块实现的。 MyBatis 中自带的这两级缓存与MyBatis 以及整个应用是运行在同一个JVM中的,共享同一块堆内存。 如果这两级缓存中的数据量较大, 则可能影响系统中其他功能的运行,所以当需要缓存大量数据时,优先考虑 |
Binding 模块 | MyBatis 通过Binding 模块将用户自定义的Mapper 接口与映射配置文件关联起来,系统可以通过调用自定义Mapper 接口中的方法执行相应的SQL 语句完成数据库操作,尽早发现拼写错误。 开发人员无须编写自定义Mapper 接口的实现, MyBatis 会自动为其创建动态代理对象。 |
配置解析 | 在MyBatis 初始化过程中,会加载mybatis-config.xml 配置文件、映射配置文件以及Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到Configuration 对象中。 之后,利用该Configuration 对象创建Sq!SessionFactory 对象。 待MyBatis 初始化之后,开发人员可以通过初始化得到Sq!SessionFactory 创建SqlSession 对象并完成数据库操作。 |
SOL 解析与scripting 模块 | MyBatis 实现动态SQL 语句的功能,提供了多种动态SQL 语句对应的节点,例如,< where>节点、< if>节点、< foreach>节点等。通过这些节点的组合使用, 开发人员可以写出几乎满足所有需求的动态SQL 语句。 My Batis 中的scripting 模块会根据用户传入的实参,解析映射文件中定义的动态SQL节点,并形成数据库可执行的SQL 语句。之后会处理SQL 语句中的占位符,绑定用户传入的实参。 |
SOL 执行 | SQL 语句的执行涉及多个组件,其中比较重要的是Executor 、StatementHandler 、ParameterHandler 和ResultSetHandler。 Executor 主要负责维护一级缓存和二级缓存,并提供事务管理的相关操作,它会将数据库相关操作委托给StatementHandler 完成。 StatementHandler 首先通过ParameterHandler 完成SQL 语句的实参绑定; 然后通过java.sql.Statement 对象执行SQL 语句并得到结果集; 最后通过ResultSetHandler 完成结果集的映射,得到结果对象并返回。(看图) |
插件 | 通过添加用户自定义插件的方式对MyBatis 进行扩展。 |
MyBatis 执行一条SQL 语句的大致过程:
核心是SqlSession 接口,该接口中定义了MyBatis 暴露给应用程序调用的API ,也就是上层应用与MyBatis 交互的桥梁。接口层在接收到调用请求时,会调用核心处理层的相应模块来完成具体的数据库操作。
实际开发系统时,我们可通过JDBC完成多种数据库操作。这里以传统JDBC编程过程中的查询操作为例进行说明,其主要步骤如下:
上述步骤(1)到步骤(4) 以及步骤(6) 在每次查询操作中都会出现,在保存、更新、删除等其他数据库操作中也有类似的重复性代码。在实践中,为了提高代码的可维护性,可以将上述重复性代码封装到一个类似DBUtils 的工具类中。步骤5 中完成了关系模型到对象模型的转换,要使用比较通用的方式封装这种复杂的转换是比较困难的。
为了解决该问题, ORM ( Object Relational Mapping ,对象-关系映射)框架应运而生。如图所示, ORM 框架的主要功能就是根据映射配置文件,完成数据在对象模型与关系模型之间的映射,同时也屏蔽了上述重复的代码,只暴露简单的API 供开发人员使用。
另外,实际生产环境中对系统的性能是有一定要求的,数据库作为系统中比较珍贵的资源,极易成为整个系统的性能瓶颈,所以我们不能像上述JDBC 操作那样简单粗暴地直接访问数据库、直接关闭数据库连接。应用程序一般需要通过集成缓存、数据源、数据库连接池等组件进行优化,如果没有ORM 框架的存在,就要求开发人员熟悉相关组件的API 井手动编写集成相关的代码,这就提高了开发难度并延长了开发周期。
Hibernate 通过hbm . xml 映射文件维护Java 类与数据库表的映射关系。
数据库中所有的表通过hbm皿nl 配置文件映射之后,都对应一个Java 类, 表中的每一行数据在运行过程中会被映射成相应的Java 对象。在Java 对象之间存在一对多、一对一、多对多等复杂的层次关系, Hibernate的hbm.xml 映射文件也可以维护这种层次关系, 并将这种关系与数据库中的外键、关联表等进行映射,这也就是所谓的“ 关联映射” 。
Hibernate 的互PI 没有侵入性,业务逻辑不需要继承Hibernate 的任何接口。
Hibernate默认提供了一级缓存和二级缓存,这有利于提高系统的性能,降低数据库压力。
很难修改Hibernate 生成的SQL 语句,当数据量比较大、数据库结构比较复杂时, Hibernate 生成SQL 语句会非常复杂,而且要让生成的SQL 语句使用正确的索引也比较困难,这就会导致出现大量慢查询的情况。
在有些大数据量、高并发、低延迟的场景下,Hibernate 并不是特别适合。
Hibernate 对批处理的支持并不是很友好,这也会影响部分性能。(出现了iBatis ( Mybatis 的前身)这种半自动化的映射方式来解决性能问题。)
JPA 仅仅是一个持久化的规范,它并没有提供具体的实现。
JPA 规范的愿景很美好, 但是并没有得到很好的发展,现在在实践中的出场率也不是很高。
严格来说, Spring JDBC 并不能算是一个ORM 框架,它仅仅是使用模板方式对原生JDBC进行了一层非常薄的封装。
Spring JDBC 中没有映射文件、对象查询语言、缓存等概念,而是直接执行原生SQL 语句。
Spring JDBC 本身就位于Spring 核心包中,也是Spring 框架的基础模块之一,天生与Spring 框架无缝集成。
相较于Hibernate, MyBatis 更加轻量级,可控性也更高,在使用MyBatis 时我们直接在映射配置文件中编写待执行的原生SQL 语句,这就给了我们直接优化SQL 语句的机会,让SQL语句选择合适的索引,能更好地提高系统的性能,比较适合大数据量、高并发等场景。
在编写SQL 语句时,我们也可以比较方便地指定查询返回的列,而不是查询所有列并映射对象后返回,这在列比较多的时候也能起到一定的优化效果。
Hibernate 生成的SQL 语句难以优化;
SpringJDBC 和MyBatis 直接使用原生SQL 语句,优化空间比较大;
My Batis 和Hibernate 有设计良好的缓存机制, 三者都可以与第三方数据源配合使用;
Hibernate 帮助开发人员屏蔽了底层数据库方言,而Spring JDBC 和My Batis 在该方面没有做很好的支持,但实践中很少有项目会来回切换底层使用的数据库产品,所以这点并不是特别重要;
Hibernate 和MyBatis 都提供了XML 映射配置文件和注解两种方式实现映射, Spring JDBC 则是通过ORM化的Callback 的方式进行映射。