4.5Java EEMyBatis缓存机制

news2024/10/5 16:28:16

一、 一级缓存

MyBatis的一级缓存级别

        MyBatis的一级缓存是SqlSession级别的缓存。如果同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis会将查询结果写入到一级缓存中,此后,如果程序没有执行插入、更新、删除操作,当第二次执行相同的查询语句时,MyBatis会直接读取一级缓存中的数据,而不用再去数据库查询,从而提高了数据库的查询效率。

举例说明MyBatis的一级缓存级别

例如,存在数据表tb_book,从表中多次查询id为1的图书信息,当程序第一次查询id为1的图书信息时,程序会将查询结果写入MyBatis一级缓存,当程序第二次查询id为1的图书信息时,MyBatis直接从一级缓存中读取,不再访问数据库进行查询。当程序对数据库执行了插入、更新、删除操作,MyBatis会清空一级缓存中的内容以防止程序误读。

案例的查询过程

        通过一个案例来对MyBatis一级缓存的应用进行详细讲解,该案例要求根据图书id查询图书信息。案例具体步骤如下。

1、在mybatis数据库中创建名为tb_book的数据表,同时预先插入几条测试数据。

USE mybatis;

# 创建一个名称为tb_book的表,并插入数据,这里只展示一条

CREATE TABLE  tb_book( 

     id INT PRIMARY KEY AUTO_INCREMENT,

     bookName VARCHAR(255),

     price double,

     author VARCHAR(40)   );

INSERT INTO tb_book(bookName,price,author) VALUES('Java基础入门',45.0,' 传智播客高教产品研发部');

INSERT INTO tb_book(bookName,price,author) VALUES('Java基础案例教程',48.0,' 程序员');

INSERT INTO tb_book(bookName,price,author) VALUES('JavaWeb程序设计任务教程',50.0,' 程序员');

 ​​​​​​​

  1. 创建持久化类Book,在Book类中定义图书id、图书名称、图书价格、图书作者属性,以及属性对应的getter/setter方法。 
    import java.io.Serializable;
    
    public class Book implements Serializable {
        private Integer id;                    //主键
        private String bookName;              //图书名称
        private double price;                 //价格
        private String author;                //作者
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getBookName() {
            return bookName;
        }
    
        public void setBookName(String bookName) {
            this.bookName = bookName;
        }
    
        public double getPrice() {
            return price;
        }
    
        public void setPrice(double price) {
            this.price = price;
        }
    
        public String getAuthor() {
            return author;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
        @Override
        public String toString() {
            return "Book{" +
                    "id=" + id + ", bookName='" + bookName +
                    ", price=" + price + ", author='" + author + '}';
        }
    }

  2. 创建图书映射文件BookMapper.xml,并在该文件中编写根据图书id查询图书信息的SQL语句。
<mapper namespace="com.mac.mapper.BookMapper">

    <!-- 根据id查询图书信息 -->

    <select id="findBookById" parameterType="Integer"

            resultType="com.mac.pojo.Book">

     SELECT * from tb_book where id=#{id} </select>

    <!-- 根据id更新图书信息 -->

    <update id="updateBook"

        parameterType="com.mac.pojo.Book">

         update tb_book set bookName=#{bookName},price=#{price} 

    where id=#{id} </update>

</mapper>
  1. 在核心配置文件mybatis-config.xml中的<mappers>标签下,引入BookMapper.xml映射文件。
<mapper resource="com/mac/mapper/BookMapper.xml" />

  1. 由于需要通过log4j日志组件查看一级缓存的工作状态,因此需要在pom.xml中引入log4j的相关依赖。
<dependency>

   <groupId>log4j</groupId>

      <artifactId>log4j</artifactId>

   <version>1.2.17</version>

</dependency>

6、创建log4j.properties文件,用于配置MyBatis和控制台的。 

#全局日志配置
log4j.rootLogger=DEBUG, Console
#控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
#日志输出级别
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

7、在测试类MyBatisTest中,编写测试方法findBookByIdTest1()。

public void findBookByIdTest1() {

    // 1.通过工具类生成SqlSession对象

    SqlSession session1 = MyBatisUtils.getSession();

    // 2.使用session1查询id为1的图书的信息

    Book book1 = session.selectOne("com.mac.mapper." 

                               + "BookMapper.findBookById", 1);

    System.out.println(book1.toString()); // 3.输出查询结果信息

    // 再次使用session1查询id为1的图书的信息,同2、3步

    // 4.关闭SqlSession

    session1.close(); 

}

MyBatis如何防止程序误读

        当程序对数据库执行了插入、更新、删除操作后,MyBatis会清空一级缓存中的内容,以防止程序误读。MyBatis一级缓存被清空之后,再次使用SQL查询语句访问数据库时,MyBatis会重新访问数据库。例如上面的例子,首先查询id为1的图书信息,然后使用更新语句对数据库中的图书信息进行更改,更改之后,再次对id为1的图书信息进行查询时,MyBatis依然会从数据库中查询。

二、二级缓存

使用二级缓存的好处

        由4.5.1节的内容可知,相同的Mapper类,相同的SQL语句,如果SqlSession不同,则两个SqlSession查询数据库时,会查询数据库两次,这样也会降低数据库的查询效率。为了解决这个问题,就需要用到MyBatis的二级缓存。MyBatis的二级缓存是Mapper级别的缓存,与一级缓存相比,二级缓存的范围更大,多个SqlSession可以共用二级缓存,并且二级缓存可以自定义缓存资源。

MyBatis二级缓存的执行过程

        在MyBatis中,一个Mapper.xml文件通常称为一个Mapper,MyBatis以namespace区分Mapper,如果多个SqlSession对象使用同一个Mapper的相同查询语句去操作数据库,在第一个SqlSession对象执行完后,MyBatis会将查询结果写入二级缓存,此后,如果程序没有执行插入、更新、删除操作,当第二个SqlSession对象执行相同的查询语句时,MyBatis会直接读取二级缓存中的数据。

MyBatis二级缓存的执行过程图解

二级缓存与一级缓存的不同点

        与MyBatis的一级缓存不同的是,MyBatis的二级缓存需要手动开启,开启二级缓存通常要完成以下两个步骤。

1、开启二级缓存的全局配置

        与使用二级缓存前,需要在MyBatis的核心配置mybatis-config.xml文件中通过<settings>元素开启二级缓存的全局配置。

<settings>

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

</settings>

2、开启当前Mapper的namespace下的二级缓存 

        开启当前Mapper的namespace下的二级缓存,可以通过MyBatis映射文件中的<cache>元素来完成。 

<!-- 开启当前Mapper的namespace下的二级缓存-->

<cache>

</cache>

默认状态的二级缓存可实现的功能

(1)映射文件中所有select语句将会被缓存。

(2)映射文件中的所有insert、update和delete语句都会刷新缓存。

(3)缓存会使用LRU算法回收。

(4)没有刷新间隔,缓存不会以任何时间顺序来刷新。

(5)缓存会存储列表集合或对象的1024个引用。

(6)缓存是可读/可写的缓存,这意味着对象检索不是共享的,缓存可以安全的被调用者修改,而不干扰其他调用者或线程所做的潜在修改。 

<cache>元素的属性  如果需要调整级缓存的特性,可通过<cache>元素的属性来实现。

属性

说明

flushInterval

刷新间隔。该属性可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况下是不设置值。

size

引用数目。该属性可以被设置为任意正整数,默认值为1024。

readOnly

只读。该属性可以被设置为true或者false。当缓存设置为只读时,缓存对象不能被修改,但此时缓存性能较高。当缓存设置为可读写时,性能较低,但安全性高。

eviction

回收策略。该属性有4个可选值。

eviction4个可选值

LRU:最近最少使用的策略。移除最长时间不被使用的对象。

FIFO:先进先出策略。按对象进入缓存的顺序来移除它们。

SOFT:软引用策略。移除基于垃圾回收器状态和软引用规则的对象。

WEAK:弱引用策略。更积极地移除基于垃圾收集器状态和弱引用规则的对象.

        

        接下来通过一个案例演示MyBatis二级缓存的应用,该案例仍旧根据id查询图书信息,案例具体步骤如下。

1、修改映射文件BookMapper.xml,在映射文件的<mapper>元素下追加编写<cache>元素开启当前Mapper的namespace的二级缓存。

<!— 开启当前BookMapper的namespace下的二级缓存-->

<cache>

</cache>

2、在测试类MyBatisTest中,编写测试方法findBookByIdTest1()。

public void findBookByIdTest3() {

    // 1.通过工具类生成两个SqlSession对象,这里只展示了一个

    SqlSession session1 = MyBatisUtils.getSession();

   // 2.使用session1查询id为1的图书的信息

    Book book1 = session1.selectOne("com.mac.mapper." 

                               + "BookMapper.findBookById", 1);

    System.out.println(book1.toString()); // 3.输出查询结果信息

    // 4.关闭SqlSession1

    session1.close(); 

}

3、执行MyBatisTest测试类的findBookByIdTest3()方法,控制台会输出结果。 

对MyBatis二级缓存的应用案例的运行结果分析

        控制台输出了执行SQL语句的日志信息以及查询结果。通过分析SQL语句日志信息可以发现,当第一个SqlSession对象session1执行查询时,Cache Hit Ratio(缓存命中率)为0,程序发送了SQL语句;当第二个SqlSession对象session2执行相同的查询时,Cache Hit Ratio为0.5,程序没有发出SQL语句,这就说明,程序直接从二级缓存中获取了数据。

多个SqlSession在同一个Mapper中执行

        在实际开发中,经常会遇到多个SqlSession在同一个Mapper中执行操作,例如,SqlSession1执行查询操作,SqlSession2执行插入、更新、删除操作,SqlSession3又执行和SqlSession1相同的查询操作。当SqlSession1执行查询操作时,程序会将查询结果写入MyBatis二级缓存,当SqlSession2对数据库执行了插入、更新、删除操作后,MyBatis会清空二级缓存中的内容,以防止程序误读。当SqlSession3执行和SqlSession1相同的查询操作时,MyBatis会重新访问数据库。

多学一招:Cache Hit Ratio(缓存命中率)

        终端用户访问缓存时,如果在缓存中查找到了要被访问的数据,就叫做命中。如果缓存中没有查找到要被访问的数据,就是没有命中。当多次执行查询操作时,缓存命中次数与总的查询次数(缓存命中次数+缓存没有命中次数)的比,就叫作缓存命中率,即缓存命中率=缓存命中次数/总的查询次数。当MyBatis开启二级缓存后,第一次查询数据时,由于数据还没有进入缓存,所以需要在数据库中查询而不是在缓存中查询,此时,缓存命中率为0。第一次查询过后,MyBatis会将查询到的数据写入缓存中,当第二次再查询相同的数据时,MyBatis会直接从缓存中获取这条数据,缓存将命中,此时的缓存命中率为0.5(1/2)。当第三次查询相同的数据,则缓存命中率为0.66666(2/3),以此类推。

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

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

相关文章

Mysql (insert,update操作)

1.创建表&#xff1a; 创建员工表employee&#xff0c;字段如下&#xff1a; id&#xff08;员工编号&#xff09;&#xff0c;name&#xff08;员工名字&#xff09;&#xff0c;gender&#xff08;员工性别&#xff09;&#xff0c;salary&#xff08;员工薪资&#xff09; …

脚踏Java知识点

对上节Java的基础语法续讲 三元运算符和if语句格式的区别 语法格式&#xff1a; 三元运算符的语法格式是&#xff1a;(condition) ? expression1 : expression2&#xff1b; if语句的语法格式是&#xff1a; if (condition) { // 执行 expression1 } else { // 执行 express…

Stage模型HarmonyOS服务卡片开发整体说明

服务卡片&#xff08;以下简称“卡片”&#xff09;是一种界面展示形式&#xff0c;可以将应用的重要信息或操作前置到卡片&#xff0c;以达到服务直达、减少体验层级的目的。卡片常用于嵌入到其他应用&#xff08;当前卡片使用方只支持系统应用&#xff0c;如桌面&#xff09;…

cyclo(Ser-Ser),23409-30-5,环(L-丝氨酰基-L-丝氨酰),具有明确的生物活性

​资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ 产品描述&#xff1a; cyclo(Ser-Ser)&#xff08;CAS号&#xff1a;23409-30-5&#xff09;&#xff0c;环二肽(2,5-哌嗪二酮)是Z小的环肽&#xff0c;许多天然环二肽化合物都具有明确的生物活性&#xff0c;例如作为抗…

什么是矢量数据库?

我们正处于人工智能革命之中。它颠覆了它所接触的任何行业&#xff0c;承诺了伟大的创新 – 但它也带来了新的挑战。对于涉及大型语言模型、生成式 AI 和语义搜索的应用程序&#xff0c;高效的数据处理变得比以往任何时候都更加重要。 所有这些新应用程序都依赖于向量嵌入&…

【Android Framework系列】5章 AMS启动流程

1 AMS简介 AMS&#xff08;Activity Manager Service&#xff09;是Android中最核心的服务&#xff0c;管理着四大组件的启动、切换、调度及应用进程的管理和调度等工作。AndroidQ将Activity移到了ActivityTaskManagerService中&#xff0c;但也和AMS相关联。 AMS通过使用一些…

3.元素的显示与隐藏

类似网站广告, 当我们点击关闭就不见了, 但是我们重新刷新页面, 会重新出现! 本质:让一个元素在页面中隐藏或者显示出来。 display显示隐藏&#xff0c;不保留原来的位置visibility 显示隐藏&#xff0c;保留原来的位置overflow 溢出显示隐藏&#xff0c;只对溢出的部分进行处…

1000道网络安全必备面试题合集,秋招金九银十必看!!!

前言 以下为网络安全各个方向涉及的面试题&#xff0c;星数越多代表问题出现的几率越大&#xff0c;祝各位都能找到满意的工作。 注&#xff1a;本套面试题&#xff0c;已整理成pdf文档&#xff0c;但内容还在持续更新中&#xff0c;因为无论如何都不可能覆盖所有的面试问题&a…

Ctfshow web入门 PHP特性篇 web89-web151 全

web入门 PHP特性篇的wp都一把梭哈在这里啦~ 有点多&#xff0c;师傅们可以收藏下来慢慢看&#xff0c;写的应该挺全面的叭… 有错误敬请斧正&#xff01; CTFshow PHP web89 看题目&#xff0c;有个flag.php文件。题目要求get有个num&#xff0c;是数字但是不包含0-9。 intv…

【opencv之cv::Mat数据深拷贝和浅拷贝探讨】

cv::Mat数据深拷贝和浅拷贝 cv::Mat 拷贝方法实验测试1.matA matSrc2.matB(matSrc)3.matC matSrc.clone()4.matSrc.copyTo(matD) 很多时候写程序除了一个强大的架构&#xff0c;细节也很重要&#xff0c;俗话说的话细节决定成败嘛&#xff0c;在使用cv::Mat做图片处理的时候发…

C#(五十六)之线程池

线程池&#xff1a; 线程池是一种多线程的形式&#xff0c;其中的任务被添加到队列中&#xff0c;并在创建线程时自动启动。 ThreadPool类&#xff1a;以下都是静态方法&#xff1a;&#xff08;不需要new的&#xff09; GetAvailableThreads剩余空闲线程数 GetMaxThreads最…

生成token的两种方式

方式一&#xff1a;自定义工具类 手动编写代码&#xff0c;写两个方法&#xff0c;一个生成&#xff0c;一个解析&#xff1b; 第一步&#xff1a;导入依赖 <!--JWT令牌依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjw…

i2c_tool的使用

文章目录 前言一、交叉编译i2c_tool二、板子上使用i2c_tool三、为什么不需要编写驱动也能够访问到对应设备四、命令行使用i2_tool操作AP3216模块五、使用i2c_tool代码操作IIC设备六、相关函数讲解1.open_i2c_dev2.int set_slave_addr 七、具体代码编写总结 前言 本篇文章将带大…

精益生产对制造业真的那么重要吗?

说到底是精益生产值不值得的问题。 重要且可得&#xff0c;那它就值得。国内的很多制造企业之所以对“精益生产”持怀疑甚至否度态度&#xff0c;大都经历过实施过程中的“水土不服”难题。抛砖引玉讲一下&#xff1a; 1、精益生产的最典型案例 1991年&#xff0c;在当时整个…

架构训练营笔记:可扩展设计

可扩展 复杂度模型 业务复杂度&#xff1a;业务固有的复杂度&#xff0c;主要体现为难以理解、难以扩展&#xff0c;例如业务数量多&#xff08;微信&#xff09;、业务流程长&#xff08;支付宝&#xff09;、业务之间关系复杂&#xff08;例如ERP&#xff09;。 质量复杂度…

基于单片机的智能空调系统的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;LCD1602液晶显示当前水温&#xff0c;定时提醒&#xff0c;水量变化DS18B20检测当前水体温度&#xff1b;水位传感器检测当前水位&#xff1b;继电器驱动加热片进行水温加热&#xff1b;定时提醒喝水&#xff0c;蜂鸣器报警&#x…

「网络编程」应用层协议_ HTTP协议学习及深入理解

「前言」文章内容大致是应用层协议的HTTP协议讲解。 「归属专栏」网络编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 「枫叶先生有点文青病」「句子分享」 俗话说&#xff0c;开弓没有回头箭&#xff0c;唯有箭折、箭落、箭中靶子三种结果而已。 ——江晓英《苏东坡&#xf…

开源代码分享(6)—考虑实时市场联动的电力零售商鲁棒定价策略(附matlab代码)

摘要&#xff1a;电力零售商作为连接电力批发市场与零售市场的桥梁&#xff0c;是电力市场化改革中的重要主体&#xff0c;其经营效率直接决定了市场化改革的成败。然而电力零售商在运营过程中面临着用电量需求和价格双重不确定性的市场风险&#xff0c;亟需通过优化市场行为以…

Qt实现画板绘制椭圆

Qt在窗体中绘图在paintEvent函数中进行,使用QPainter类进行窗体绘制 如果只是简单的在paintevent中向画布绘制椭圆,由于实时绘制的许多个椭圆重合在一起,就会出现下面的情况 你可以在每次绘制椭圆之前调用清空画布 myPix->fill(Qt::white);但是又会出现下面的情况,无法…

一篇万字博客带你入门layUI

今日金句 心里种花&#xff0c;人生才不会荒芜 文章目录 一、什么是layui二、layui、easyui与bootstrap的对比2.1 layui和bootstrap对比&#xff08;这两个都属于UI渲染框架&#xff09;2.2 layui和easyui对比 三、layui入门3.1 引入3.2 入门案例&#xff1a;点击弹出框3.3 经…