什么是 ORM
ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数据与简单 Java 对象(POJO)的映射关系的技术。简单的说,ORM 是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
什么是 Mybatis
MyBatis 是一个开源、轻量级的数据持久化框架,是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC,简化了加载驱动、创建连接、创建 statement 等繁杂的过程,开发者只需要关注 SQL 语句本身。
MyBatis 的主要思想是将程序中的大量 SQL 语句剥离出来,使用 XML 文件或注解的方式实现 SQL 的灵活配置,将 SQL 语句与程序代码分离,在不修改程序代码的情况下,直接在配置文件中修改 SQL 语句。
Mybaits的优缺点
- 优点:
- 基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML 标签,支持编写动态 SQL 语句,并可重用。
- 与 JDBC 相比,减少了50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接;
- 很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持)。
- 能够与 Spring 很好的集成;
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
- 缺点:
- SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写 SQL 语句的功底有一定要求。
- SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
MyBatis 与 Hibernate
Hibernate 和 MyBatis 都是目前业界中主流的对象关系映射(ORM)框架,它们的主要区别如下:
- sql 优化方面:
- Hibernate 使用
HQL
(Hibernate Query Language)语句,独立于数据库。不需要编写大量的 SQL,就可以完全映射,但会多消耗性能,且开发人员不能自主的进行 SQL 性能优化。提供了日志、缓存、级联(级联比 MyBatis 强大)等特性。 - MyBatis 需要手动编写
SQL
,所以灵活多变。支持动态 SQL、处理列表、动态生成表名、支持存储过程。工作量相对较大。
- Hibernate 使用
- 开发方面:
- Hibernate 属于
全自动 ORM
映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。 - Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为
半自动 ORM
映射工具。
- Hibernate 属于
- 缓存机制比较:
- Hibernate 的二级缓存配置在
SessionFactory
生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置缓存。 - MyBatis 的二级缓存配置在
每个具体的表-对象映射中
进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且 Mybatis 可以在命名空间中共享相同的缓存配置和实例,通过 Cache-ref 来实现。
- Hibernate 的二级缓存配置在
MyBatis 功能架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O9xFAXmd-1643528322284)(https://tse1-mm.cn.bing.net/th/id/R-C.9ebd9cca1a58f457f95309bb932285b5?rik=BQlbOAU6avu%2bHg&riu=http%3a%2f%2fupload-images.jianshu.io%2fupload_images%2f9033085-45f641094a702061.png&ehk=657sIb6y6fNU8PX0cKer7U1qgZy4edXMYNWK%2b4O6ut8%3d&risl=&pid=ImgRaw&r=0)]
我们把 Mybatis 的功能架构分为三层:
- API接口层:提供给外部使用的接口 API,开发人员通过这些本地 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
- 数据处理层:负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
- 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
Mybatis 框架架构
这张图从上往下看。MyBatis 的初始化,会从
mybatis-config.xml
配置文件,解析构造成Configuration
这个类,就是图中的红框。
- **加载配置:**配置来源于两个地方,一处是配置文件,一处是 Java 代码的注解,将 SQL 的配置信息加载成为一个个
MappedStatement
对象(包括了传入参数映射配置、执行的 SQL 语句、结果映射配置),存储在内存中。 - **SQL解析:**当 API 接口层接收到调用请求时,会接收到传入 SQL 的 ID 和传入对象(可以是 Map、JavaBean 或者基本数据类型),Mybatis 会根据 SQL 的 ID 找到对应的 MappedStatement,然后根据传入参数对象对 MappedStatement 进行解析,解析后可以得到最终要执行的 SQL 语句和参数。
- **SQL执行:**将最终得到的 SQL 和参数拿到数据库进行执行,得到操作数据库的结果。
- **结果映射:**将操作数据库的结果按照映射的配置进行转换,可以转换成 HashMap、JavaBean 或者基本数据类型,并将最终结果返回。
Mybatis 缓存机制
缓存机制减轻数据库压力,提高数据库性能。Mybatis 的缓存分为两级:一级缓存
、二级缓存
。默认情况下,MyBatis 只开启一级缓存。
从上图我们可以看出缓存顺序如下:
- 先看二级缓存中有没有;
- 再看一级缓存中有没有;
- 查询数据库;
对 Mybaits 缓存体系有大致了解后,我们接下来再看看啥是一级缓存与二级缓存。
一级缓存
一级缓存是基于 PerpetualCache
(MyBatis 自带)的 HashMap 本地缓存,缓存的数据只在 SqlSession
内有效。每个 SqlSession 中持有了 Executor
,每个 Executor 中有一个 LocalCache
。当用户发起查询时,MyBatis 根据当前执行的语句生成 MappedStatement
,在 Local Cache
进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入 Local Cache
,最后返回结果给用户。具体执行过程如下图所示。
注意:
1、如果 SqlSession 执行了 DML
操作(insert、update、delete),并 commit 了,那么 mybatis 就会清空当前 SqlSession 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异。
2、当 session flush(刷新)或者 close(关闭)之后,该 session 中所有的缓存就会被清空。
二级缓存
二级缓存是 mapper
级别的缓存,也就是同一个 namespace 的 mapper.xml
,当多个 SqlSession 使用同一个 Mapper 操作数据库的时候,得到的数据会缓存在同一个二级缓存区域。
二级缓存默认是没有开启的。需要在 setting 全局参数中配置开启二级缓存。开启二级缓存后,会使用 CachingExecutor
装饰 Executor,进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询,具体的工作流程如下所示。
开启二级缓存步骤:
1、MyBatis 的全局缓存配置需要在 mybatis-config.xml
的 settings 元素中设置,代码如下:
<settings>
<setting name="cacheEnabled" value="true" />
</settings>
2、在 mapper 文件(如 userMapper.xml
)中设置缓存,代码如下:
<mapper namescape="com.user.UserMapper">
<!-- cache 配置 -->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true" />
...
</mapper>
以上属性说明如下:
属性 | 说明 |
---|---|
eviction | 代表的是缓存回收策略,目前 MyBatis 提供以下策略。 LRU:使用较少,移除最长时间不用的对象; FIFO:先进先出,按对象进入缓存的顺序来移除它们; SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象; WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。 |
flushInterval | 刷新间隔时间,单位为毫秒,这里配置的是 100 秒刷新,如果省略该配置,那么只有当 SQL 被执行的时候才会刷新缓存。 |
size | 引用数目,正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是 1024 个对象。 |
readOnly | 只读,默认值为 false,意味着缓存数据只能读取而不能修改,这样设置的好处是可以快速读取缓存,缺点是没有办法修改缓存。 |
3、若想禁用当前 select
语句的二级缓存,添加 useCache="false"
修改如下:
<select id="getCountByName" parameterType="java.util.Map" resultType="INTEGER" statementType="CALLABLE" useCache="false">
注意:
1、如果 SqlSession
执行了 DML 操作(insert、update、delete)
,并 commit
了,那么 mybatis
就会清空当前 mapper 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异;
总结一级缓存与二级缓存
- MyBatis的二级缓存相对于一级缓存来说,实现了
SqlSession
之间缓存数据的共享,同时粒度更加的细,能够到namespace
级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。 - MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
- 在分布式环境下,由于默认的 MyBatis Cache 实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将 MyBatis 的 Cache 接口实现,有一定的开发成本,直接使用 Redis、Memcached 等分布式缓存可能成本更低,安全性也更高。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/78362.html