ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数据与简单 Java 对象(POJO)的映射关系的技术。简单的说,ORM 是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
MyBatis 是一个开源、轻量级的数据持久化框架,是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC,简化了加载驱动、创建连接、创建 statement 等繁杂的过程,开发者只需要关注 SQL 语句本身。
MyBatis 的主要思想是将程序中的大量 SQL 语句剥离出来,使用 XML 文件或注解的方式实现 SQL 的灵活配置,将 SQL 语句与程序代码分离,在不修改程序代码的情况下,直接在配置文件中修改 SQL 语句。
Hibernate 和 MyBatis 都是目前业界中主流的对象关系映射(ORM)框架,它们的主要区别如下:
HQL
(Hibernate Query Language)语句,独立于数据库。不需要编写大量的 SQL,就可以完全映射,但会多消耗性能,且开发人员不能自主的进行 SQL 性能优化。提供了日志、缓存、级联(级联比 MyBatis 强大)等特性。SQL
,所以灵活多变。支持动态 SQL、处理列表、动态生成表名、支持存储过程。工作量相对较大。全自动 ORM
映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。半自动 ORM
映射工具。SessionFactory
生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置缓存。每个具体的表-对象映射中
进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且 Mybatis 可以在命名空间中共享相同的缓存配置和实例,通过 Cache-ref 来实现。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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 的功能架构分为三层:
这张图从上往下看。MyBatis 的初始化,会从
mybatis-config.xml
配置文件,解析构造成Configuration
这个类,就是图中的红框。
MappedStatement
对象(包括了传入参数映射配置、执行的 SQL 语句、结果映射配置),存储在内存中。缓存机制减轻数据库压力,提高数据库性能。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
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 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异;
SqlSession
之间缓存数据的共享,同时粒度更加的细,能够到namespace
级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。