MyBatis一级缓存和二级缓存全面详解(1)

还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!

王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。

对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!

【完整版领取方式在文末!!】

93道网络安全面试题

需要体系化学习资料的朋友,可以加我V获取:vip204888 (备注网络安全)

内容实在太多,不一一截图了

黑客学习资源推荐

最后给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

😝朋友们如果有需要的话,可以联系领取~

1️⃣零基础入门
① 学习路线

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

image

② 路线对应学习视频

同时每个成长路线对应的板块都有配套的视频提供:

image-20231025112050764

2️⃣视频配套工具&国内外网安书籍、文档
① 工具

② 视频

image1

③ 书籍

image2

资源较为敏感,未展示全面,需要的最下面获取

在这里插入图片描述在这里插入图片描述

② 简历模板

在这里插入图片描述

因篇幅有限,资料较为敏感仅展示部分资料,添加上方即可获取👆

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

文章目录

Mybatis的缓存机制

2.1 缓存介绍

像大多数的持久化框架一样, Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。Mybatis 中缓存分为一级缓存,二级缓存。

1、默认情况下,只有一级缓存(session级别的缓存,也称为本地缓存)开启。

2、二级缓存需要手动开启和配置(默认支持状态),他是基于Mapper级别的缓存。

3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

在这里插入图片描述

从图中我们可以看出:一级缓存是基于SqlSessoion的缓存,一级缓存的内容不能跨sqlsession。由MyBatis自动维护。二级缓存是基于映射文件的缓存,缓存范围比一级缓存更大。不同的sqlsession可以访问二级缓存的内容。哪些数据放入二级缓存需要自己指定。

2.2 一级缓存

一级缓存,也叫本地缓存,作用域默认为当前的SqlSession也就是说不同的SqlSession的缓存是不同的。当 Session flush 或 close 后, 该Session中的一级缓存将被清空。一级缓存是MyBatis维护的,并且默认开启,且不能被关闭,但可以调用clearCache()来清空缓存,或者改变缓存的作用域;

2.2.1 一级缓存相关参数
  • localCacheScope
    • SESSION:会话级别缓存,默认值
    • STATEMENT:语句级别缓存,缓存只对当前执行的语句生效(相当于关闭一级缓存)

在这里插入图片描述

2.2.2 一级缓存测试
  • 实体类:
package com.dfbz.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/\*\*
 \* @author lscl
 \* @version 1.0
 \* @intro:
 \*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp implements Serializable {
    private Integer id;
    private String name;
    private Integer age;
    private String addr;
    private Double salary;
    private Double deptId;
}

  • 在EmpDao中准备方法:
package com.dfbz.dao;

import com.dfbz.entity.Emp;

/\*\*
 \* @author lscl
 \* @version 1.0
 \* @intro:
 \*/
public interface EmpDao {

    Emp findById(Integer id);

    void save(Emp emp);
}

  • EmpDao.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 名称空间,指定对哪个接口进行映射-->
<mapper namespace="com.dfbz.dao.EmpDao">

    <!--
 flushCache:每次执行完SQL语句是否要刷新缓存
 true: 要刷新缓存(清空缓存)
 false: 不清空缓存
-->
    <!--
 useCache: 是否要将SQL语句的结果集存入二级缓存
 true: 存入二级缓存(默认值)
 false: 不存
 -->
    <select id="findById" resultType="emp">
        select * from emp where id=#{id}
    </select>

    <!--
 增删改的flushCache可以设置不清空二级缓存
 -->
    <insert id="save" flushCache="false">
        insert into emp(id,name) values(null,#{name});
    </insert>
</mapper>

  • 测试类:
/\*\*
 \* 测试一级缓存
 \*/
@Test
public void test1() {

    // 获取session(开启一级缓存)
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    // 发送SQL查询数据,将数据存入一级缓存
    Emp emp = mapper.findById(1);
    System.out.println(emp);

    // 从一级缓存中获取数据(没有发送SQL)
    Emp emp2 = mapper.findById(1);
    System.out.println(emp2);
    System.out.println(emp == emp2);            // true

    // session关闭,一级缓存清空
    session.close();
}

观察日志:

在这里插入图片描述

2.2.3 一级缓存失效清空

一级缓存在如下情况下,会情况:

  • 1)将一级缓存的作用域设置为语句级别(localCacheScope设置为STATEMENT
  • 2)清空缓存(clearCache)
  • 3)执行任何增删改操作都会导致整个一级缓存(并不是清除影响的那行缓存,而是整个一级缓存)
  • 4)刷新缓存(flushCache)
  • 5)session关闭,一级缓存清空
2.2.3.1 设置localCacheScope
<!--日志配置-->
<settings>
    <setting name="logImpl" value="STDOUT\_LOGGING"/>

    <!--
 更改一级缓存的作用域
 SESSION: 会话级别缓存,默认值
 STATEMENT: 语句级别缓存,缓存只对当前执行的语句生效(类似于关闭一级缓存)
 -->
    <setting name="localCacheScope" value="STATEMENT"/>
</settings>

  • 再次执行上述的测试代码:
/\*\*
 \* 测试一级缓存
 \*/
@Test
public void test1() {

    // 获取session(开启一级缓存)
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    // 发送SQL查询数据
    Emp emp = mapper.findById(1);
    System.out.println(emp);

    // 发送SQL查询数据
    Emp emp2 = mapper.findById(1);
    System.out.println(emp2);
    System.out.println(emp == emp2);            // false

    // session关闭
    session.close();
}

执行程序,观察控制台:

在这里插入图片描述

2.2.3.2 clearCache

一级缓存清空测试:

@Test
public void test2() throws Exception {          // 测试clearCache

    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    // 首先从一级缓存里面查询,没查询到,然后去数据库查询,将查询的结果放入一级缓存
    Emp emp = mapper.findById(1);
    System.out.println(emp);

    session.clearCache();           // 清空一级缓存

    // 首先从一级缓存里面查询(没有),去数据库查询,将查询的结果放入一级缓存
    Emp emp2 = mapper.findById(1);
    System.out.println(emp2);

    System.out.println(emp == emp2);        // false

    // 关闭session(一级缓存清空)
    session.close();
}

执行结果,查看控制台:

在这里插入图片描述

2.2.3.3 关闭session清空缓存

一级缓存是基于session级别的,如果session关闭,那么一级缓存将会失效!

  • 测试代码:
@Test
public void test3() throws Exception {          // 测试session关闭清空一级缓存

    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    // 将查询的结果放入一级缓存
    Emp emp = mapper.findById(1);
    System.out.println(emp);

    // session关闭一级缓存清空(session关闭了代表与数据库的会话一级结束了,不可以再发送任何的SQL语句了)
    session.close();

    // 重新获取一次会话
    SqlSession session2 = factory.openSession();
    EmpDao mapper2 = session2.getMapper(EmpDao.class);

    // 新的session的一级缓存中并没有数据,发送SQL去数据库查询
    Emp emp2 = mapper2.findById(1);
    System.out.println(emp2);
    System.out.println(emp == emp2);

    // session关闭,一级缓存清空
    session2.close();
}

执行程序,查看控制台:

在这里插入图片描述

2.2.3.4 执行任何的增删改清空缓存

在MyBatis中,执行任何的增删改都会导致一级缓存清空!

  • 测试代码:
@Test
public void test3() throws Exception {          // 测试session关闭清空一级缓存

    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    // 将查询的结果放入一级缓存
    Emp emp = mapper.findById(1);
    System.out.println(emp);

    // session关闭一级缓存清空(session关闭了代表与数据库的会话一级结束了,不可以再发送任何的SQL语句了)
    session.close();

    // 重新获取一次会话
    SqlSession session2 = factory.openSession();
    EmpDao mapper2 = session2.getMapper(EmpDao.class);

    // 新的session的一级缓存中并没有数据,发送SQL去数据库查询
    Emp emp2 = mapper2.findById(1);
    System.out.println(emp2);
    System.out.println(emp == emp2);

    // session关闭,一级缓存清空
    session2.close();
}

在这里插入图片描述

2.2.3.5 flushCache清空缓存

flushCache属性用于控制执行完操作后是否要刷新缓存,对于增删改语句,flushCache的值默认是true,对于查询语句,flushCache的值默认是false,但是MyBatis不支持将任何的增删改语句设置为false

  • EmpDao.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.dfbz.dao.EmpDao">

    <!--
 flushCache="true":每次执行完这个查询语句都清空一级缓存
 -->
    <select id="findById" resultType="emp" flushCache="true">
        select * from emp where id=#{id}
    </select>

    <!--
 flushCache: 执行完本次SQL语句是否要刷新缓存
 insert/update/delete:默认为true,对于增删改的操作,MyBatis不支持将其设置为false
 select:默认为false
 -->
    <insert id="save" flushCache="false">
        insert into emp values(null,#{name},#{age},#{addr},#{salary},null)
    </insert>
</mapper>

Tips:在MyBatis中,不支持将任何的增删改语句的flushCache属性设置为false

  • 测试代码:
@Test
public void test5() throws Exception {          // 测试flushCache清空缓存(对于增删改操作无法设置为false)

    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    Emp emp = mapper.findById(1);
    System.out.println(emp);

    Emp emp2 = mapper.findById(1);
    System.out.println(emp2);

    System.out.println(emp == emp2);

    // 关闭session(一级缓存清空)
    session.close();
}

执行程序,观察控制台:

在这里插入图片描述

需要注意的是MyBatis并不支持将任何的增删改语句的flushCache设置为false

  • 测试代码(此时save语句的flushCache为false):
/\*\*
 \* 测试增删改的flushCache属性
 \*/
@Test
public void test6() {

    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao empDao = session.getMapper(EmpDao.class);

    // 将查询到的结果存入一级缓存缓存(发送一次SQL)
    Emp emp = empDao.findById(1);       // flushCache="true",因此会清空一级缓存
    System.out.println(emp);

    // 进行增删改操作(一级缓存清空,即使设置了flushCache=false也不管用)
    empDao.save(new Emp(null, "test", 20, null, null, null));

    // 重新发送SQL语句
    Emp emp2 = empDao.findById(1);
    System.out.println(emp2);

    // session关闭,一级缓存清空
    session.close();
}

在这里插入图片描述

2.2.4 一级缓存的引用

MyBatis将数据存入一级缓存时,是将对象的引用(内存地址)存入一级缓存;在获取一级缓存中的数据时,MyBatis将返回当初存入一级缓存的那个内存地址值,也就是说,一级缓存中的数据是同一个;这样一来就会出现内存地址值引用问题;

  • 测试代码:
// 缓存的引用
@Test
public void test6() throws Exception {

    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    // 去数据库查询,将查询结果存入一级缓存
    Emp emp = mapper.findById(1);
    System.out.println(emp);                // name=张三

    // 修改emp对象的name为abc(一级缓存中的emp也会修改)
    emp.setName("abc");

    // 从一级缓存中查询emp对象
    Emp emp2 = mapper.findById(1);
    System.out.println(emp2);               // name=abc

    System.out.println(emp == emp2);

    // 关闭session(一级缓存清空)
    session.close();
}

2.2.5 PerpetualCache缓存类

MyBatis 跟缓存相关的类都在cache 包里面,其中有一个Cache 接口,只有一个默认的实现类 PerpetualCache,改类是MyBatis的缓存实现类;包括一级缓存和二级缓存都是采用PerpetualCache类来实现的;

在这里插入图片描述

  • 测试代码:
@Test
public void test1() throws Exception {
    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    /\*
 首先从一级缓存里面查询
 查询到了: 返回
 没查询到: 去数据库查询,之后将查询的结果放入一级缓存
 \*/
    Emp emp = mapper.findById(1);
    System.out.println(emp);

    /\*
 从一级缓存里面查询,直接返回
 \*/
    Emp emp2 = mapper.findById(1);
    System.out.println(emp2);

    System.out.println(emp == emp2);        // true

    // 关闭session(一级缓存清空)
    session.close();
}

一级缓存执行流程:

在这里插入图片描述

2.3 二级缓存

我们刚刚学习完了一级缓存,一级缓存是session级别的缓存,不同的session一级缓存是不能共享的;

二级缓存是mapper级别的缓存,多个session去操作同一个Mapper映射的SQL语句时,多个session可以共用二级缓存,二级缓存是跨SqlSession 的。

当二级缓存和一级缓存同时存在时,先查询二级缓存,再查询一级缓存;

在这里插入图片描述

2.3.1 二级缓存相关配置
  • 全局配置:

MyBatis默认是开启二级缓存的,我可以通过cacheEnabled参数来控制二级缓存的开关;此配置默认开启

  • 修改SqlMapConfig配置文件:
<settings>
    <!--日志配置-->
    <setting name="logImpl" value="STDOUT\_LOGGING"/>

    <!--开启二级缓存,默认是开启状态,false为关闭二级缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>

  • Mapper配置以及SQL语句配置:

在SqlMapConfig.xml开启二级缓存后(此配置默认开启状态),还需要在对应的mapper.xml文件中激活缓存;否则二级缓存并不会生效;

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dfbz.dao.EmpDao">
    <!--
 <cache />: 开启当前Mapper的二级缓存
 eviction: 当内存使用紧张时缓存的回收策略
 • LRU – 最近最少使用的:移除最长时间不被使用的对象。
 • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
 • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
 • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
 flushInterval:缓存刷新间隔,缓存多长时间清空一次,默认不清空,可以通过此参数设置一个毫秒值
 readOnly:是否只读
 true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
 mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
 false:非只读:mybatis觉得获取的数据可能会被修改。
 mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
 size:二级缓存中最大能够缓存存放多少元素
 type:指定自定义缓存的全类名(实现Cache接口)
 -->
    <cache />
    
      <!--
 useCache: 当一级缓存关闭时,是否将本次SQL语句的结果集存入二级缓存
 true: 存入(默认值)
 false: 不存入
 -->
    <select id="findById" resultType="emp">
        select * from emp where id=#{id}
    </select>

    <insert id="save" >
        insert into emp(id,name) values(null,#{name});
    </insert>
</mapper>

存入二级缓存中的对象必须实现序列化接口:

在这里插入图片描述

2.3.2 二级缓存测试
2.3.2.1 二级缓存代码测试

当二级缓存和一级缓存同时存在时,先查询二级缓存,再查询一级缓存;

当session关闭时,将一级缓存中的数据写入二级缓存;

@Test
public void test1() throws Exception {          // 测试二级缓存

    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

/\*
 先从二级缓存里面查询-->再查询一级缓存-->再查询数据库(查询到了把结果放入一级缓存)
 \*/
    Emp emp = mapper.findById(1);
    System.out.println(emp);

    // 关闭一级缓存,把数据写入二级缓存(这个时候才会把数据写入二级缓存(序列化))
    session.close();
// session.clearCache(); // session关闭才会将一级缓存的内容写入二级缓存,clearCache并不会

    // 开启一级缓存
    SqlSession session2 = factory.openSession();

    EmpDao mapper2 = session2.getMapper(EmpDao.class);

    // 先查询二级缓存(有),反序列化
    Emp emp2 = mapper2.findById(1);
    System.out.println(emp2);
    System.out.println(emp == emp2);            // false ,两个对象的属性值一样,但是内存地址值不一样(反序列化出来的)

    session2.close();
}

观察日志:

在这里插入图片描述

2.3.2.2 useCache

useCache可以控制当前SQL语句的结果集是否要存入二级缓存;默认清空下为true

<!--
 useCache: 当前SQL语句的结果集是否存入二级缓存(需要当前的Mapper.xml开启二级缓存useCache才会生效)
 true: 存
 false: 不存
-->
<select id="findById" resultType="emp" useCache="true">
    select * from emp where id=#{id}
</select>

Tips:

  • 1)useCache只能控制二级缓存,并不会影响一级缓存;
  • 2)useCache需要当前的mapper.xml开启二级缓存功能后才能使用;
2.3.3 二级缓存失效情况

二级缓存在如下情况下,会情况:

  • 1)执行任何增删改操作
  • 2)刷新缓存(flushCache)
2.3.3.1 执行增删改

执行任何的增删改操作,不仅会导致一级缓存清空,也会导致二级缓存清空!

  • 测试代码:
/\*\*
 \* 测试任何增删改清空二级缓存
 \*/
@Test
public void test2() throws Exception {

    // 开启一级缓存
    SqlSession session = factory.openSession(true);

    EmpDao mapper = session.getMapper(EmpDao.class);

    // 先从二级缓存里面查询,再从一级缓存里面查询,再从数据库查询(发送SQL),将查询到的结果集存入一级缓存
    Emp emp = mapper.findById(1);
    System.out.println(emp);

    // 关闭一级缓存,把数据写入二级缓存(这个时候才会把数据写入二级缓存(序列化))
    session.close();

    // 开启一级缓存
    SqlSession session2 = factory.openSession(true);

    EmpDao mapper2 = session2.getMapper(EmpDao.class);

    Emp saveEmp = new Emp();
    saveEmp.setName("aaa");

    mapper2.save(saveEmp);                  // 执行任何的增删改清空所有缓存(一级缓存和二级缓存)

    // 查询二级缓存(没有),一级缓存(没有),发送SQL
    Emp emp2 = mapper2.findById(1);
    System.out.println(emp2);

    session2.close();
}

执行程序,日志如下:

Created connection 368342628.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
==>  Preparing: select * from dept where id=?			# 第一次发送SQL
==> Parameters: 1(Integer)
<==    Columns: id, name, location
<==        Row: 1, 研发部, 中国台湾
<==      Total: 1
研发部
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]		# 关闭session,将数据写入二级缓存
Returned connection 368342628 to pool.
Opening JDBC Connection
Checked out connection 368342628 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
==>  Preparing: insert into dept values(null,?,?)			# 执行任何增删改清空一级/二级缓存
==> Parameters: test(String), test(String)
<==    Updates: 1
Cache Hit Ratio [com.dfbz.dao.DeptDao]: 0.5
==>  Preparing: select * from dept where id=?				# 再次发送SQL查询
==> Parameters: 1(Integer)
<==    Columns: id, name, location
<==        Row: 1, 研发部, 中国台湾
<==      Total: 1
false		# 返回false
Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
Returned connection 368342628 to pool.
Disconnected from the target VM, address: '127.0.0.1:58814', transport: 'socket'

Process finished with exit code 0

2.3.3.2 flushCache

flushCache不仅会清空一级缓存,而且还会清空二级缓存

在这里插入图片描述

Tips:flushCache控制二级缓存时可以设置任何的增删改查是否清空二级缓存

  • 测试代码:
/\*
 测试flushCache
 \*/
@Test
public void test4() throws Exception {

    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    // 先从二级缓存里面查询,再从一级缓存里面查询,再从数据库查询(发送SQL),之后flushCache,一级缓存数据清空
    Emp emp = mapper.findById(1);
    System.out.println(emp);

    // 一级缓存关闭,将一级缓存的数据写入二级缓存(此时一级缓存并没有数据)
    session.close();

    // 获取session,开启新的一级缓存
    SqlSession session2 = factory.openSession();

    EmpDao mapper2 = session2.getMapper(EmpDao.class);

    // 此时二级缓存,一级缓存均没有数据,最终去数据库查询
    Emp emp2 = mapper2.findById(1);
    System.out.println(emp2);

    session2.close();
}

执行程序,日志如下:

Created connection 527829831.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
==>  Preparing: select * from emp where id=?			# 发送SQL语句查询,并没有将结果存入一级缓存(flushCache)
==> Parameters: 1(Integer)
<==    Columns: id, name, age, addr, salary, dept_id
<==        Row: 1, 张三, 20, 广西来宾, 7600.00, 1
<==      Total: 1
Emp(id=1, name=张三, age=20, addr=广西来宾, salary=7600.0, deptId=null)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]

# sesison关闭,将一级缓存的数据写入二级缓存(此时一级缓存中并没有数据)
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]	
Returned connection 527829831 to pool.
Cache Hit Ratio [com.dfbz.dao.EmpDao]: 0.5
Opening JDBC Connection									# 重新开启一个session
Checked out connection 527829831 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
# 先查询二级缓存(没查询到),再擦好像一级缓存(没查询到),最终去查询数据库
==>  Preparing: select * from emp where id=?			
==> Parameters: 1(Integer)
<==    Columns: id, name, age, addr, salary, dept_id
<==        Row: 1, 张三, 20, 广西来宾, 7600.00, 1
<==      Total: 1
Emp(id=1, name=张三, age=20, addr=广西来宾, salary=7600.0, deptId=null)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Disconnected from the target VM, address: '127.0.0.1:52082', transport: 'socket'

Process finished with exit code 0

flushCache不仅会清空一级缓存,而且也会清空二级缓存

  • 扩展一个方法:
Emp findByName(String name);

  • EmpDao.xml:
<select id="findByName" resultType="emp" >
    select * from emp where name=#{name}
</select>

在这里插入图片描述

  • 测试代码:
@Test
public void test5() throws Exception {          

    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    // 先从二级缓存里面查询,再从一级缓存里面查询,再从数据库查询(发送SQL),将查询到的结果集存入一级缓存
    Emp emp = mapper.findByName("小明");
    System.out.println(emp);

    // 一级缓存关闭,将一级缓存的数据写入二级缓存
    session.close();

    // 获取session,开启新的一级缓存
    SqlSession session2 = factory.openSession();

    EmpDao mapper2 = session2.getMapper(EmpDao.class);

    // flushCache清空一级缓存(不会清空二级缓存)
    Emp emp2 = mapper2.findById(1);
    System.out.println(emp2);

    // 先查询二级缓存(查询到了)
    Emp emp3 = mapper2.findByName("小明");
    System.out.println(emp3);

    session2.close();
}

执行程序,日志如下:

Created connection 527829831.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]

# 将数据写入一级缓存
==>  Preparing: select * from emp where name=?
==> Parameters: 小明(String)
<==    Columns: id, name, age, addr, salary, dept_id
<==        Row: 3, 小明, 25, 广东云浮, 6600.00, 2
<==      Total: 1
Emp(id=3, name=小明, age=25, addr=广东云浮, salary=6600.0, deptId=null)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]

# 连接关闭,将数据写入二级缓存
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Cache Hit Ratio [com.dfbz.dao.EmpDao]: 0.0
Opening JDBC Connection
Checked out connection 527829831 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]

# 执行findById(flushCache清空一级缓存和二级缓存)
==>  Preparing: select * from emp where id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, age, addr, salary, dept_id
<==        Row: 1, 张三, 20, 广西来宾, 7600.00, 1
<==      Total: 1
Emp(id=1, name=张三, age=20, addr=广西来宾, salary=7600.0, deptId=null)
Cache Hit Ratio [com.dfbz.dao.EmpDao]: 0.3333333333333333

# 再次发送SQL去数据库查询
==>  Preparing: select * from emp where name=?
==> Parameters: 小明(String)
<==    Columns: id, name, age, addr, salary, dept_id
<==        Row: 3, 小明, 25, 广东云浮, 6600.00, 2
<==      Total: 1
Emp(id=3, name=小明, age=25, addr=广东云浮, salary=6600.0, deptId=null)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Disconnected from the target VM, address: '127.0.0.1:52207', transport: 'socket'

Process finished with exit code 0

前面我们学习一级缓存的时候,flushCache对于增删改语句是无法设置为false(设置了不生效),即执行任何增删改的时候一定会清空一级缓存,但flushCache却可以控制二级缓存的增删改;

将insert语句的flushCache设置为false(不清空缓存):

在这里插入图片描述

  • 测试代码:
@Test
public void test6() throws Exception {

    // 开启一级缓存
    SqlSession session = factory.openSession();

    EmpDao mapper = session.getMapper(EmpDao.class);

    // 先从二级缓存里面查询,再从一级缓存里面查询,再从数据库查询(发送SQL),将查询到的结果集存入一级缓存
    Emp emp = mapper.findByName("小明");
    System.out.println(emp);

    // 一级缓存关闭,将一级缓存的数据写入二级缓存
    session.close();

    // 获取session,开启新的一级缓存
    SqlSession session2 = factory.openSession();

    EmpDao mapper2 = session2.getMapper(EmpDao.class);

    // 执行新增(flushCache为false,并不会清空二级缓存)
    mapper2.save(new Emp(null,"xxx",null,null,null,null));

    // 先查询二级缓存(查询到了)
    Emp emp2 = mapper2.findByName("小明");
    System.out.println(emp2);

    session2.close();
}

日志如下:

Created connection 527829831.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
==>  Preparing: select * from emp where name=?
==> Parameters: 小明(String)
<==    Columns: id, name, age, addr, salary, dept_id
<==        Row: 3, 小明, 25, 广东云浮, 6600.00, 2
<==      Total: 1
Emp(id=3, name=小明, age=25, addr=广东云浮, salary=6600.0, deptId=null)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Opening JDBC Connection
Checked out connection 527829831 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
==>  Preparing: insert into emp(id,name) values(null,?);
==> Parameters: xxx(String)
<==    Updates: 1
Cache Hit Ratio [com.dfbz.dao.EmpDao]: 0.5
Emp(id=3, name=小明, age=25, addr=广东云浮, salary=6600.0, deptId=null)
Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Disconnected from the target VM, address: '127.0.0.1:52373', transport: 'socket'

Process finished with exit code 0

2.3.4 PerpetualCache的二级缓存

PerpetualCache不仅是MyBaits的一级缓存类,同时MyBatis也将二级缓存的数据存储在PerpetualCache中;在MyBatis加载时(创建session工厂时)就已经创建好了二级缓存,只不过需要等到session关闭时,本次session的一级缓存的数据才会写入二级缓存;

  • 测试代码:
package com.dfbz.mybatis;

import com.dfbz.dao.EmpDao;
import com.dfbz.entity.Emp;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/\*\*
 \* @author lscl
 \* @version 1.0
 \* @intro:
 \*/
public class Demo02\_一级缓存和二级缓存 {

    public static void main(String[] args) throws Exception {

        // 创建Session工厂构建对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        // 通过构建对象来构建一个session工厂(创建二级缓存)
        SqlSessionFactory factory = builder.build(Resources.getResourceAsStream("SqlMapConfig.xml"));

        // 获取session(创建一级缓存)
        SqlSession session = factory.openSession();

        // 获取mapper类
        EmpDao mapper = session.getMapper(EmpDao.class);

        // 查询二级缓存,没查询到,再查询一级缓存,没查询到,最后发送SQL去数据库查询,然后将数据放入一级缓存
        Emp emp = mapper.findById(1);
        System.out.println(emp);

        // 查询二级缓存,没查询到,再查询一级缓存,查询到了,返回(没有发送SQL)



还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!


王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。


对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!


【完整版领取方式在文末!!】


***93道网络安全面试题***


![](https://img-blog.csdnimg.cn/img_convert/6679c89ccd849f9504c48bb02882ef8d.png)








![](https://img-blog.csdnimg.cn/img_convert/07ce1a919614bde78921fb2f8ddf0c2f.png)





![](https://img-blog.csdnimg.cn/img_convert/44238619c3ba2d672b5b8dc4a529b01d.png)



**需要体系化学习资料的朋友,可以加我V获取:vip204888 (备注网络安全)**

内容实在太多,不一一截图了


### 黑客学习资源推荐


最后给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!


对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

#### 1️⃣零基础入门


##### ① 学习路线


对于从来没有接触过网络安全的同学,我们帮你准备了详细的**学习成长路线图**。可以说是**最科学最系统的学习路线**,大家跟着这个大的方向学习准没问题。


![image](https://img-blog.csdnimg.cn/img_convert/acb3c4714e29498573a58a3c79c775da.gif#pic_center)


##### ② 路线对应学习视频


同时每个成长路线对应的板块都有配套的视频提供:


![image-20231025112050764](https://img-blog.csdnimg.cn/874ad4fd3dbe4f6bb3bff17885655014.png#pic_center)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618540462)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值