当前位置: 首页 > news >正文

Mybatis的一级缓存和二级缓存

目录

  • 前言
  • 一级缓存
  • 1、缓存级别
  • 2、开启方式
  • 3、缓存对象
  •  4、执行过程 
  • 5、实验检验
  • 6、注意事项
  • 二级缓存
  • 1、缓存级别
  • 2、开启方式
  • 3、缓存对象
  •  4、执行过程
  • 5、实验检验 
  • 6、注意事项

前言

        MyBatis是常见的Java数据库访问层框架。在日常工作中,开发人员多数情况下是使用MyBatis的默认缓存配置,但是MyBatis缓存机制有一些不足之处,在使用中容易引起脏数据,形成一些潜在的隐患。个人在业务开发中也处理过一些由于MyBatis缓存引发的开发问题,带着个人的兴趣,来深入了解一下缓存机制

一级缓存

1、缓存级别

sqlSession级别,同一个sqlSession内共用缓存

2、开启方式

默认开启

3、缓存对象

缓存的是对象的引用,底层通过HashMap缓存结果,key为CacheKey,value为结果对象的引用

 4、执行过程 

         在一级缓存中,当sqlSession进行查询的时候,会先去查询一级缓存,如果命中则直接返回数据,否则就去查询数据库,然后再把数据写到一级缓存中

5、实验检验

        实验1:命中一级缓存 

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));
}

          实验2:修改操作会刷新缓存(flushCache标签默认为 true)

public void addStudent() throws Exception {
        SqlSession sqlSession = factory.openSession(true);
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

        System.out.println(studentMapper.getStudentById(1));
        System.out.println("增加了" + studentMapper.addStudent(buildStudent()) + "个学生");
        System.out.println(studentMapper.getStudentById(1));
}

         实验3:缓存的是对象的引用

public void getStudentById() throws Exception {
        SqlSession sqlSession = factory.openSession(true);
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

        Student s = studentMapper.getStudentById(1);
        System.out.println(s);
        s.setName("名字已修改");
        System.out.println(studentMapper.getStudentById(1));
}

6、注意事项

        ①当有Insert、Delete、Update语句执行的时候,会将一级缓存清空,因为他们的flushCache标签默认为 true

        ②显示调用sqlSession.clearCache()

        ③sqlSession关闭也会清空缓存

二级缓存

1、缓存级别

namespace级别,也就是说一个mapper中的所有SQLSession都共用的一个二级缓存

2、开启方式

<!--mybatis的全局设置-->
<settings>
    <!--开启二级缓存,默认是开启的为true-->
    <setting name="cacheEnabled" value="true"/>
</settings>

3、缓存对象

序列化后的对象数据

 4、执行过程

        一级缓存中,其最大的共享范围就是一个SqlSession内部,如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。

        开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,如果二级缓存未命中,则去查询一级缓存,如果一级缓存也未命中,再去查数据库,然后将结果写入一级缓存,当sqlSession关闭或者提交事务后,一级缓存就会被刷到二级缓存中。

        所以数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。

5、实验检验 

        实验1:基于mapper级别的缓存

public void testCacheWithCommitOrClose() throws Exception {
        SqlSession sqlSession1 = factory.openSession(true); 
        SqlSession sqlSession2 = factory.openSession(true); 
        
        StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
        StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
        
        System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));
        sqlSession1.commit();
        System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));
}

        实验2:更新操作会刷新缓存

public void testCacheWithUpdate() throws Exception {
        SqlSession sqlSession1 = factory.openSession(true); 
        SqlSession sqlSession2 = factory.openSession(true); 
        SqlSession sqlSession3 = factory.openSession(true); 
        
        StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
        StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
        StudentMapper studentMapper3 = sqlSession3.getMapper(StudentMapper.class);
        
        System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));
        sqlSession1.commit();
        System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));
        studentMapper3.updateStudentName("方方",1);
        sqlSession3.commit();
        System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));
}

6、注意事项

        ①当有Insert、Delete、Update语句执行的时候,也会将二级缓存清空

        ②当sqlSession关闭或者提交事务后,数据会缓存到二级缓存

相关文章:

  • 【MySQL】库和表的操作
  • css的背景图和背景色混用;rgba和opacity区别
  • 如何使用logrotete定时切割mysql的慢日志
  • 【SpringMVC】深入解析使用 Postman 在请求中传递对象类型、数组类型、参数类型的参数方法和后端参数重命名、及非必传参数设置的方法
  • Spring Cloud Alibaba 实战:Sentinel 保障微服务的高可用性与流量防护
  • React 性能优化
  • 使用Python实现量子计算实验工具的全景指南
  • WHAT - 前端性能指标
  • 记录一次解决日期格式引起的SQL失效的问题
  • Python Flask 开发用于访问数据库的 REST API
  • 深度学习与大模型-矩阵的运算
  • Trae AI 自动升级项目Nuxt版本
  • 其他元素的乘积(前后缀积)
  • C++ 编程指南27 - 始终将 mutex 与它所保护的数据一起定义,并尽可能使用 synchronized_value<T>
  • Linux网络 五种 IO 模型
  • Netty基础—2.网络编程基础四
  • Linux运维(三)Linux命令行操作:从菜鸟到高手
  • 【性能测试】Jmeter如何做一份测试报告(3)
  • C++ 布尔类型(bool)深度解析
  • PHP+redis 优雅实现加锁机制
  • “80后”北大硕士罗婕履新甘肃宁县县委常委、组织部部长
  • 打造信息消费新场景、新体验,上海信息消费节开幕
  • 讲座|消逝之钟:《红楼梦》与《布登勃洛克一家》中的时间观
  • 董军在第六届联合国维和部长级会议上作大会发言
  • 把中国声音带向世界,DG和Blue Note落户中国
  • 国防部:菲方应停止一切侵权挑衅危险举动,否则只会自食苦果