GitChat 作者:凯伦 
原文:一步步学习 Mybatis:缓存的使用及源码分析 
关注微信公众号:GitChat 技术杂谈 ,一本正经的讲技术

【不要错过文末活动】

前言

基于个人的兴趣,开了这场chat,主题是Mybatis一级和二级缓存的应用及源码分析。希望在本场chat结束后,能够帮助读者朋友明白以下三点。

  1. Mybatis是什么。

  2. Mybatis一级和二级缓存如何配置使用。

  3. Mybatis一级和二级缓存的工作流程及源码分析。

本次分析中涉及到的代码和数据库表均放在Github上,地址: mybatis-cache-demo

目录

为达到以上三个目的,本文按照以下顺序展开。

  1. Mybatis的基础概念。

  2. 一级缓存介绍及相关配置。

  3. 一级缓存工作流程及源码分析。

  4. 一级缓存总结。

  5. 二级缓存介绍及相关配置。

  6. 二级缓存源码分析。

  7. 二级缓存总结。

  8. 全文总结。

Mybatis的基础概念

本章节会对Mybatis进行大体的介绍,分为官方定义和核心组件介绍。

首先是Mybatis官方定义,如下所示。

MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以对配置和原生Map使用简单的XML或注解,将接口和Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

其次是Mybatis的几个核心概念。

  1. SqlSession : 代表和数据库的一次会话,向用户提供了操作数据库的方法。

  2. MappedStatement: 代表要发往数据库执行的指令,可以理解为是Sql的抽象表示。

  3. Executor: 具体用来和数据库交互的执行器,接受MappedStatement作为参数。

  4. 映射接口: 在接口中会要执行的Sql用一个方法来表示,具体的Sql写在映射文件中。

  5. 映射文件: 可以理解为是Mybatis编写Sql的地方,通常来说每一张单表都会对应着一个映射文件,在该文件中会定义Sql语句入参和出参的形式。

下图就是一个针对Student表操作的接口文件StudentMapper,在StudentMapper中,我们可以若干方法,这个方法背后就是代表着要执行的Sql的意义。

 架构 | 一步步学习 Mybatis:缓存的使用及源码分析 AI教程 第1张

通常也可以把涉及多表查询的方法定义在StudentMapper中,如果查询的主体仍然是Student表的信息。也可以将涉及多表查询的语句单独抽出一个独立的接口文件。

在定义完接口文件后,我们会开发一个Sql映射文件,主要由mapper元素和select|insert|update|delete元素构成,如下图所示。

 架构 | 一步步学习 Mybatis:缓存的使用及源码分析 AI教程 第2张

mapper元素代表这个文件是一个映射文件,使用namespace和具体的映射接口绑定起来,namespace的值就是这个接口的全限定类名。select|insert|update|delete代表的是Sql语句,映射接口中定义的每一个方法也会和映射文件中的语句通过id的方式绑定起来,方法名就是语句的id,同时会定义语句的入参和出参,用于完成和Java对象之间的转换。

在Mybatis初始化的时候,每一个语句都会使用对应的MappedStatement代表,使用namespace+语句本身的id来代表这个语句。如下代码所示,使用mapper.StudentMapper.getStudentById代表其对应的Sql。

SELECT id,name,age FROM student WHERE id = #{id}1

在Mybatis执行时,会进入对应接口的方法,通过类名加上方法名的组合生成id,找到需要的MappedStatement,交给执行器使用。

至此,Mybatis的基础概念介绍完毕。

一级缓存

一级缓存介绍

在系统代码的运行中,我们可能会在一个数据库会话中,执行多次查询条件完全相同的Sql,鉴于日常应用的大部分场景都是读多写少,这重复的查询会带来一定的网络开销,同时select查询的量比较大的话,对数据库的性能是有比较大的影响的。

如果是Mysql数据库的话,在服务端和Jdbc端都开启预编译支持的话,可以在本地JVM端缓存Statement,可以在Mysql服务端直接执行Sql,省去编译Sql的步骤,但也无法避免和数据库之间的重复交互。关于Jdbc和Mysql预编译缓存的事情,可以看我的这篇博客JDBC和Mysql那些事

Mybatis提供了一级缓存的方案来优化在数据库会话间重复查询的问题。实现的方式是每一个SqlSession中都持有了自己的缓存,一种是SESSION级别,即在一个Mybatis会话中执行的所有语句,都会共享这一个缓存。一种是STATEMENT级别,可以理解为缓存只对当前执行的这一个statement有效。如果用一张图来代表一级查询的查询过程的话,可以用下图表示。

 架构 | 一步步学习 Mybatis:缓存的使用及源码分析 AI教程 第3张

每一个SqlSession中持有了自己的Executor,每一个Executor中有一个Local Cache。当用户发起查询时,Mybatis会根据当前执行的MappedStatement生成一个key,去Local Cache中查询,如果缓存命中的话,返回。如果缓存没有命中的话,则写入Local Cache,最后返回结果给用户。

一级缓存配置

上文介绍了一级缓存的实现方式,解决了什么问题。在这个章节,我们学习如何使用Mybatis的一级缓存。只需要在Mybatis的配置文件中,添加如下语句,就可以使用一级缓存。共有两个选项,SESSION或者STATEMENT,默认是SESSION级别。

<setting name="localCacheScope" value="SESSION"/>1

一级缓存实验

配置完毕后,通过实验的方式了解Mybatis一级缓存的效果。每一个单元测试后都请恢复被修改的数据。 
首先是创建了一个示例表student,为其创建了对应的POJO类和增改的方法,具体可以在entity包和Mapper包中查看。

CREATE TABLE `student` (  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,  `name` varchar(200) COLLATE utf8_bin DEFAULT NULL,  `age` tinyint(3) unsigned DEFAULT NULL,  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;123456

在以下实验中,id为1的学生名称是凯伦。

实验1

开启一级缓存,范围为会话级别,调用三次getStudentById,代码如下所示:

public void getStudentById() throws Exception {
        SqlSession sqlSession = factory.openSession(true); // 自动提交事务
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        System.out.println(studentMapper.getStudentById(1));
        System.out.println(studentMapper.getStudentById(1));
        System.out.println(studentMapper.getStudentById(1));
    }1234567

二维码

文章来源:CSDN,欢迎分享,转载请保留出处
本文链接:http://www.52ai.com/AIjiaocheng/3470.html
吾爱智能52AI(http://www.52ai.com/)遵循行业规范,欢迎加入中国最大的AI交流群:626784247 入群或者关注左侧微信公众号即可获取每天AI最新资源教程等!