初学mybatis(七)缓存

news2024/11/18 11:19:52

学习回顾:初学mybatis(六)

一、简介

1、什么是缓存 [ Cache ]?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
    2、为什么使用缓存?
  • 减少和数据库的交互次数,减少系统开销,提高系统效率。
    3、什么样的数据能使用缓存?
  • 经常查询并且不经常改变的数据。

二、Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可
    以极大的提升查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
  • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
  • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

三、一级缓存

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

四、测试

1、在mybatis中加入日志,方便测试结果
2、编写接口方法

//根据id查询用户
User queryUserById(@Param("id") int id);

3、接口对应的Mapper文件

<select id="queryUserById" resultType="user">
 select * from user where id = #{id}
</select>

4、测试

@Test
public void testQueryUserById(){
  SqlSession session = MybatisUtils.getSession();
  UserMapper mapper = session.getMapper(UserMapper.class);

  User user = mapper.queryUserById(1);
  System.out.println(user);
  User user2 = mapper.queryUserById(1);
  System.out.println(user2);
  System.out.println(user==user2);

  session.close();
}

5、结果分析
在这里插入图片描述

五、一级缓存失效的四种情况

一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;

一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!

1、sqlSession不同

@Test
public void testQueryUserById(){
  SqlSession session = MybatisUtils.getSession();
  SqlSession session2 = MybatisUtils.getSession();
  UserMapper mapper = session.getMapper(UserMapper.class);
  UserMapper mapper2 = session2.getMapper(UserMapper.class);

  User user = mapper.queryUserById(1);
  System.out.println(user);
  User user2 = mapper2.queryUserById(1);
  System.out.println(user2);
  System.out.println(user==user2);

  session.close();
  session2.close();
}

观察结果:发现发送了两条SQL语句!
结论:每个sqlSession中的缓存相互独立
2、sqlSession相同,查询条件不同

@Test
public void testQueryUserById(){
  SqlSession session = MybatisUtils.getSession();
  UserMapper mapper = session.getMapper(UserMapper.class);
  UserMapper mapper2 = session.getMapper(UserMapper.class);

  User user = mapper.queryUserById(1);
  System.out.println(user);
  User user2 = mapper2.queryUserById(2);
  System.out.println(user2);
  System.out.println(user==user2);

  session.close();
}

观察结果:发现发送了两条SQL语句!很正常的理解
结论:当前缓存中,不存在这个数据
3、sqlSession相同,两次查询之间执行了增删改操作!
增加方法

//修改用户
int updateUser(Map map);

编写SQL

<update id="updateUser" parameterType="map">
 update user set name = #{name} where id = #{id}
</update>

测试

@Test
public void testQueryUserById(){
  SqlSession session = MybatisUtils.getSession();
  UserMapper mapper = session.getMapper(UserMapper.class);

  User user = mapper.queryUserById(1);
  System.out.println(user);

  HashMap map = new HashMap();
  map.put("name","xiaotang");
  map.put("id",4);
  mapper.updateUser(map);

  User user2 = mapper.queryUserById(1);
  System.out.println(user2);

  System.out.println(user==user2);

  session.close();
}

观察结果:查询在中间执行了增删改操作后,重新执行了
结论:因为增删改操作可能会对当前数据产生影响
4、sqlSession相同,手动清除一级缓存

@Test
public void testQueryUserById(){
  SqlSession session = MybatisUtils.getSession();
  UserMapper mapper = session.getMapper(UserMapper.class);

  User user = mapper.queryUserById(1);
  System.out.println(user);

  session.clearCache();//手动清除缓存

  User user2 = mapper.queryUserById(1);
  System.out.println(user2);

  System.out.println(user==user2);

  session.close();
}

一级缓存就是一个map

六、二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
  • 工作机制
  • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
  • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
  • 新的会话查询信息,就可以从二级缓存中获取内容;
  • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

使用步骤

1、开启全局缓存 【mybatis-config.xml】

<setting name="cacheEnabled" value="true"/>
<cache/>

官方示例=====>查看官方文档
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

3、代码测试

  • 所有的实体类先实现序列化接口

  • 测试代码

@Test
public void testQueryUserById(){
  SqlSession session = MybatisUtils.getSession();
  SqlSession session2 = MybatisUtils.getSession();

  UserMapper mapper = session.getMapper(UserMapper.class);
  UserMapper mapper2 = session2.getMapper(UserMapper.class);

  User user = mapper.queryUserById(1);
  System.out.println(user);
  session.close();

  User user2 = mapper2.queryUserById(1);
  System.out.println(user2);
  System.out.println(user==user2);

  session2.close();
}

结论

  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中
  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

缓存原理图

在这里插入图片描述

EhCache

第三方缓存实现–EhCache: 查看百度百科

Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;

要在应用程序中使用Ehcache,需要引入依赖的jar包

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
   <groupId>org.mybatis.caches</groupId>
   <artifactId>mybatis-ehcache</artifactId>
   <version>1.1.0</version>
</dependency>

在mapper.xml中使用对应的缓存即可

<mapper namespace = “org.acme.FooMapper” >
   <cache type = “org.mybatis.caches.ehcache.EhcacheCache” />
</mapper>

编写ehcache.xml文件,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
        updateCheck="false">
   <!--
      diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
      user.home – 用户主目录
      user.dir – 用户当前工作目录
      java.io.tmpdir – 默认临时文件路径
    -->
   <diskStore path="./tmpdir/Tmp_EhCache"/>
   
   <defaultCache
           eternal="false"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="259200"
           memoryStoreEvictionPolicy="LRU"/>

   <cache
           name="cloud_user"
           eternal="false"
           maxElementsInMemory="5000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/>
   <!--
      defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
    -->
   <!--
     name:缓存名称。
     maxElementsInMemory:缓存最大数目
     maxElementsOnDisk:硬盘最大缓存个数。
     eternal:对象是否永久有效,一但设置了,timeout将不起作用。
     overflowToDisk:是否保存到磁盘,当系统当机时
     timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
     timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
     diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
     diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
     diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
     memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
     clearOnFlush:内存数量最大时是否清除。
     memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
     FIFO,first in first out,这个是大家最熟的,先进先出。
     LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
     LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
  -->

</ehcache>

合理的使用缓存,可以让我们程序的性能大大提升!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/705375.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6应用层-6.2【实验】【计算机网络】

6应用层-6.2【实验】【计算机网络】 前言推荐6应用层知识一、DNS常用记录类型&#xff1a;二、nslookup命令的用法 6.1 Web服务与FTP服务配置6.2 DNS域名系统配置实验目的实验内容及实验环境实验原理实验过程1.搭建如图所示的网络拓扑2.测试网络连通性3.在服务器上启用DNS服务&…

腾讯云部署tomcat问题--使用localhost访问没问题但是使用公网访问就有问题

1、使用localhost访问没问题。 2、使用公网访问就有问题 3、解决方案 3.1设置了一个规则&#xff0c; 3.2查看页面&#xff0c;访问OK

如何在 Shadow 插件化框架中动态加载和调用插件中的方法

Shadow是一种Android插件化框架&#xff0c;它允许将应用功能模块以插件的方式集成到宿主应用中。插件化是指将应用的不同模块打包成独立的插件&#xff0c;可以在运行时动态地加载和卸载这些插件&#xff0c;从而实现动态扩展和灵活组合功能。 Shadow插件化框架采用了类加载器…

Jenkins全栈体系(一)

Jenkins Jenkins&#xff0c;原名 Hudson&#xff0c;2011年改为现在的名字。它是一个开源的实现持续集成的软件工具。 第一章 GitLab安装使用 官方网站&#xff1a;https://about.gitlab.com/ 安装所需最小配置 内存至少4G https://docs.gitlab.cn/jh/install/requireme…

上海声通团队在WeNet中开源Branchformer

上海声通信息科技股份有限公司作为交互式人工智能市场的领导者&#xff0c;具有极强的技术优势和突出的产品特点。公司基于自研的融合通信及人工智能两项核心技术&#xff0c;打造了丰富的、高度标准化的产品模块&#xff0c;为客户提供高效、稳定的产品体验。公司主要的业务场…

Arduino Proteus仿真空气净化器温湿度PM2.5空气质量MQ135-0049

Arduino Proteus仿真空气净化器温湿度PM2.5空气质量MQ135-0049 Proteus仿真小实验&#xff1a; Arduino Proteus仿真空气净化器温湿度PM2.5空气质量MQ135-0049 功能&#xff1a; 硬件组成&#xff1a;ARDUINO -UNO-R3开发板、 LCD1602 、DHT11温湿度传感器、电位器模拟PM2.…

垃圾收集策略与算法

垃圾收集策略与算法 程序计数器、虚拟机栈、本地方法栈随线程而生&#xff0c;也随线程而灭&#xff1b;栈帧随着方法的开始而入栈&#xff0c;随着方法的结束而出栈。这几个区域的内存分配和回收都具有确定性&#xff0c;在这几个区域内不需要过多考虑回收的问题&#xff0c;因…

详解C++类型转换特性(代码+详解)

C类型转换 引言1. C语言中的类型转换2. 为什么C需要四种类型转换 C强制类型转换1.static_cast补充 2.dynamic_cast3.const_cast4.reinterpret_cast RTTI 引言 1. C语言中的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配…

虚拟机Centos7环境下如何安装wget

一、wget简介 wget 是一个从网络上自动下载文件的自由工具&#xff0c;支持通过 HTTP、HTTPS、FTP 三个最常见的 TCP/IP协议 下载&#xff0c;并可以使用 HTTP 代理。“wget” 这个名称来源于 “World Wide Web” 与 “get” 的结合。所谓自动下载&#xff0c;是指 wget 可以在…

JVM oop内存模型

一、oop模型 1、非数组对象 InstaceOopDesc 2、数组对象 arrayOopDesc 2.1 基本数据类型数组 typeArrayOopDesc 2.2 引用类型数组 objArrayOopDesc 3、MarkOopDesc 存放锁信息、分代年龄等 二、对象的内存结构 对象内存结构分成三大部分 对象头 &#xff08;64位操作系统&a…

软考02原码反码和补码

文章目录 前言一、原码二、反码三、补码总结 前言 机器是通过二进制来存储数据的&#xff0c;最好是在学习了软考01进制转换基础上开始学习原码反码和补码。 一、原码 原码通常以固定位数表示,不足补0&#xff0c;由于需要区分正负数所以&#xff0c;最高位为符号位(0为正&…

Electron中启动node服务

记一次遇到的问题&#xff0c;我们知道Electron 中主进程是在node环境中&#xff0c;所以打算在node环境中再启动一个node服务。但是直接使用exec命令启动就会卡主。对应的代码如下 // 启动Node server const startServer async () > {try {console.log(开始启动node serv…

React | 再战Redux

✨ 个人主页&#xff1a;CoderHing &#x1f5a5;️ React.js专栏&#xff1a;React.js 再战Redux &#x1f64b;‍♂️ 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; &#x1f4ab; 系列专栏&#xff1a;吊打面试官系列 16天学会Vue 7天学会微信小程序 Node专栏…

chatgpt赋能python:下载Python的方法及使用指南

下载Python的方法及使用指南 Python是一种高级编程语言&#xff0c;被广泛应用于各种领域。如果你是一名程序员或者对编程有兴趣&#xff0c;那么学习Python会是一个不错的选择。本文将介绍Python的下载方法&#xff0c;并提供使用Python的基础指南。 Python的下载方法 Pyth…

Istio与Mcp Server服务器讲解与搭建演示

01Istio与外部注册中心 Istio为何需要对接外部注册中心 Istio 对 Kubernetes 具有较强的依赖性&#xff1a; 1.服务发现就是基于 Kubernetes 实现的&#xff0c;如果要使用 Istio&#xff0c;首先需要迁移到 Kubernetes 上&#xff0c;并使用 Kubernetes 的服务注册发现机制…

【数据挖掘】时间序列教程【二】

2.4 示例&#xff1a;颗粒物浓度 在本章中&#xff0c;我们将使用美国环境保护署的一些空气污染数据作为运行样本。该数据集由 2 年和 5 年空气动力学直径小于或等于 3.2017 \&#xff08;mu\&#xff09;g/m\&#xff08;^2018\&#xff09; 的颗粒物组成。 我们将特别关注来自…

认识GCC

GNU GNU是Linux系统下的一些工具包&#xff0c;GNU是GNU is Not Unix的缩写&#xff0c;因为当年Unix收费后&#xff0c;理查德马修斯托曼打算做一套GNU操作系统&#xff0c;当时GNU的工具包已经写好&#xff0c;就差内核即可组装成一个完整的操作系统&#xff0c;正好Linux写…

跨链 vs 多链

跨链 dApp 可以在部署在多个不同区块链上的多个不同智能合约上运行&#xff0c;而多链 dApp 则可以在不同网络上以多个单独的版本部署。 由于对区块空间的需求不断增加&#xff0c;Web3 应用层现在存在于数百个不同的区块链、二层网络和应用链上。这种现实催生了两个新术语——…

【教程】解决php微擎中的goto加密解密,一键解密工具

今天&#xff0c;我将向大家揭秘一款神奇的工具——goto解密工具&#xff0c;轻松解密这个看似棘手的问题。 无数开发者都曾因为php中的goto功能而头疼不已。goto解密工具其中之一就是解密goto代码。通过精妙的算法和强大的解析能力&#xff0c;它能够解密被goto加密的代码段&…

Vue项目设置网站小徽标

一、预期效果 自定义Vue项目的网站小徽标&#xff0c;用于显示网站的logo&#xff0c;效果大致如下 二、制作 .ico文件 2.1 打开比特虫官网 比特虫官网&#xff1a;https://www.bitbug.net/ 2.2 操作步骤如图 三、引入Vue项目 3.1 将生成的 .ico文件放入我们的 Vue 项目 3.…