Java Mybatis一级缓存和二级缓存

 更新时间:2023年04月07日 09:10:30   作者:会洗碗的CV工程师  
缓存是内存当中一块存储数据的区域,目的是提高查询效率,降低服务器和数据库的压力,这篇文章主要介绍了Mybatis一级缓存和二级缓存,感兴趣的同学可以参考阅读本文

一、什么是缓存 

        缓存是内存当中一块存储数据的区域,目的是提高查询效率。MyBatis会将查询结果存储在缓存当中,当下次执行相同的SQL时不访问数据库,而是直接从缓存中获取结果,从而减少服务器的压力。

什么是缓存?

存在于内存中的一块数据。

缓存有什么作用?

减少程序和数据库的交互,提高查询效率,降低服务器和数据库的压力。

什么样的数据使用缓存?

经常查询但不常改变的,改变后对结果影响不大的数据。

MyBatis缓存分为哪几类?

 一级缓存和二级缓存

如何判断两次Sql是相同的?

查询的Sql语句相同 传递的参数值相同 对结果集的要求相同 预编译的模板Id相同

二、Mabtis一级缓存

MyBatis一级缓存也叫本地缓存。SqlSession对象中包含一个Executor对象,Executor对象中包含一个PerpetualCache对象,在该对象存放一级缓存数据。

由于一级缓存是在SqlSession对象中,所以只有使用同一个SqlSession对象操作数据库时才能共享一级缓存。

MyBatis的一级缓存是默认开启的,不需要任何的配置。

如下图所示:

(1)测试一级缓存

其实测试方法很简单,就是通过使用相同和不同的SqlSession对象进行SQL查询,返回的对象的哈希值是否一样就可以知道了,如果返回的哈希值一样说明没有进行SQL查询,而是直接从缓存拿到对象来返回

下面是使用相同的Session对象来执行查询,如果观察user1和user2的哈希值一样则说明确实开启了一级缓存,并没有进行查询,而是直接从缓存中拿数据。 

import com.mybatisstudy.mapper.UserMapper;
import com.mybatisstudy.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
 
import java.io.InputStream;
 
public class TestUserMapper3 {
    // 测试使用同一个SqlSession查询
    @Test
    public void testCache1() throws Exception{
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        SqlSession session = factory.openSession();
 
        // 使用同一个SqlSession查询
        UserMapper mapper1 = session.getMapper(UserMapper.class);
        UserMapper mapper2 = session.getMapper(UserMapper.class);
 
        User user1 = mapper1.findById(1);
        System.out.println(user1.hashCode());
        System.out.println("------------------------------------------");
        User user2 = mapper2.findById(1);
        System.out.println(user2.hashCode());
 
        session.close();
    }
}

执行结果 

        OK,确实返回的哈希值都是一样的,并且我们可以通过控制台输出显示它并没有进行查询而是直接从缓存中拿到对象并返回,所以这就是一级缓存 ,提高了查询效率。

        下面使用不同的SqlSession来进行测试一下,返回的哈希值是否一致 

// 测试使用不同SqlSession查询
    @Test
    public void testCache2() throws Exception{
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        SqlSession session1 = factory.openSession();
        SqlSession session2 = factory.openSession();
 
        // 测试使用不同SqlSession查询
        UserMapper mapper1 = session1.getMapper(UserMapper.class);
        UserMapper mapper2 = session2.getMapper(UserMapper.class);
 
        User user1 = mapper1.findById(1);
        System.out.println(user1.hashCode());
        System.out.println("---------------------------------");
        User user2 = mapper2.findById(1);
        System.out.println(user2.hashCode());
 
        session1.close();
        session2.close();
    }

运行结果

        OK,可以看到,返回的哈希值不一样,并且从控制台输出显示也可以看到这里的确也进行了一次查询,因此可以证实,共享一级缓存确实是基于SqlSession对象的 

(2)清空一级缓存

        但是吧,如果缓存过多的话,也是会影响我们的查询效率的,所以这时候就需要清空缓存了,就像我们时不时要清理一下手机缓存否则就会很卡,是同样的道理,那怎么清空一级缓存呢?

进行以下操作可以清空MyBatis一级缓存:

  • SqlSession 调用 close() :操作后SqlSession对象不可用,该对象的缓存数据也不可用。
  • SqlSession 调用 clearCache() / commit() :操作会清空一级缓存数据。
  • SqlSession 调用增删改方法:操作会清空一级缓存数据,因为增删改后数据库发生改变,缓存数据将不准确
// 清空Mybatis一级缓存
    @Test
    public void testCache3() throws Exception{
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        SqlSession session = factory.openSession();
 
        UserMapper mapper1 = session.getMapper(UserMapper.class);
        UserMapper mapper2 = session.getMapper(UserMapper.class);
 
        User user1 = mapper1.findById(1);
        System.out.println(user1.hashCode());
 
        // 清空Mybatis一级缓存
        session.clearCache();
 
        System.out.println("-------------------------------");
        User user2 = mapper2.findById(1);
        System.out.println(user2.hashCode());
 
        session.close();
    }

执行效果

        OK,返回的哈希值也确实不一样, 但是我们有没有观察到这和上面使用不同的SqlSession对象来执行查询的时候,控制台输入显示有点不一样,那就是这里不用再建立JDBC连接,也有效了提高查询效率,所以我们偶尔还是要清空一下缓存才行

三、Mybatis二级缓存

MyBatis二级缓存也叫全局缓存。数据存放在SqlSessionFactory中,只要是同一个工厂对象创建的SqlSession,在进行查询时都能共享数据。一般在项目中只有一个SqlSessionFactory对象,所以二级缓存的数据是全项目共享的。
MyBatis一级缓存存放的是对象,二级缓存存放的是对象的数据。所以要求二级缓存存放的POJO必须是可序列化的,也就是要实现Serializable接口。
MyBatis二级缓存默认不开启,手动开启后数据先存放在一级缓存中,只有一级缓存数据清空后,数据才会存到二级缓存中。           
SqlSession 调用 clearCache() 无法将数据存到二级缓存中。

(1)开启二级缓存

1. POJO类实现Serializable接口

import java.io.Serializable;
 
public class User implements Serializable {
    private int id;
    private String username;
    private String sex;
    private String address;
}

2. 在Mybatis配置文件添加如下设置

<!-- 二级缓存打开 -->
<settings>   
 <setting name="cacheEnabled" value="true"/>
</settings>

这里有个额外知识,就是Mybatis配置文件的标签还得按照顺序来放的,否则就会以下编译错误;

The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,

objectWrapperFactory?,reflectorFactory?,plugins?,environments?,

databaseIdProvider?,mappers?)". 

  同时也说明了放置顺序就得按照match里面的顺序来放

3. 添加 <cache /> 标签

 如果查询到的集合中对象过多,二级缓存只能缓存1024个对象引用。可以通过

<cache /> 标签的size属性修改该数量。

比如:<cache size="2048"/>

(2)测试二级缓存

        那怎么测试呢,从上面我们可以知道二级缓存存放的是对象的数据,并且是基于SqlSessionFactory的,因此我们可以用SqlSessionFactory获取两个SqlSession对象,然后让他们分别获取各自的mapper,然后进行查询,返回到同一个实例化的USer对象中,如果返回的数据是一致的,但是对象的哈希值是不一样的话,则说明二级缓存里存放的确实对象的数据而不是对象。

// 测试二级缓存
@Test
public void testCache4() throws Exception {
    InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(is);
    SqlSession session = factory.openSession();

    UserMapper mapper1 = session.getMapper(UserMapper.class);
    UserMapper mapper2 = session.getMapper(UserMapper.class);

    User user1 = mapper1.findById(1);
    System.out.println(user1);
    System.out.println(user1.hashCode());

    // 让一级缓存失效
    session.commit();
    System.out.println("----------------------------");

    user1 = mapper2.findById(1);
    System.out.println(user1);
    System.out.println(user1.hashCode());
}

运行结果 

        OK,从运行结果上我们可以知道结果集返回到同一个对象中,而他们的哈希值反而不一样,说明执行第二次查询的时候新建了一个对象并且该对象指向那个对象并且将SqlSessionFactory中的数据赋值到新建的那个对象。其实从控制台打印的日志我们也可以得出,并没有执行查询方法,因为没有打印SQL语句,而且缓存也是从0.0改成了0.5,因此我们可以断定二级缓存存放的是数据而不是对象。

到此这篇关于Java Mybatis一级缓存和二级缓存的文章就介绍到这了,更多相关Mybatis一级缓存和二级缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java设计模式-单例模式实现方法详解

    java设计模式-单例模式实现方法详解

    单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例
    2021-07-07
  • springboot将lib和jar分离的操作方法

    springboot将lib和jar分离的操作方法

    本文介绍了如何通过优化pom.xml配置来减小Spring Boot项目的jar包大小,主要通过使用spring-boot-maven-plugin和maven-dependency-plugin插件,将依赖库打包到jar中,并通过指定外部lib路径的方式运行jar,从而减小jar包体积,感兴趣的朋友一起看看吧
    2025-02-02
  • Spring Boot整合Spring Data Jpa代码实例

    Spring Boot整合Spring Data Jpa代码实例

    这篇文章主要介绍了Spring Boot整合Spring Data Jpa代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • 基于Redisson实现注解式分布式锁的示例代码

    基于Redisson实现注解式分布式锁的示例代码

    这篇文章主要为大家详细介绍了如何基于Redisson实现注解式分布式锁,文中的示例代码讲解详细,具有一定的参考价值,需要的可以了解一下
    2023-07-07
  • Java 对HashMap进行排序的三种常见方法

    Java 对HashMap进行排序的三种常见方法

    这篇文章主要介绍了Java 对HashMap进行排序的三种常见方法,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-10-10
  • SpringBoot中实现订单30分钟自动取消的三种方案分享

    SpringBoot中实现订单30分钟自动取消的三种方案分享

    在电商和其他涉及到在线支付的应用中,通常需要实现一个功能:如果用户在生成订单后的一定时间内未完成支付,系统将自动取消该订单,本文将详细介绍基于Spring Boot框架实现订单30分钟内未支付自动取消的几种方案,并提供实例代码,需要的朋友可以参考下
    2023-10-10
  • Spring Boot项目中使用OpenAI-Java的示例详解

    Spring Boot项目中使用OpenAI-Java的示例详解

    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程,这篇文章主要介绍了Spring Boot项目中使用OpenAI-Java的示例详解,需要的朋友可以参考下
    2023-04-04
  • 详解Spring注解 @Configuration

    详解Spring注解 @Configuration

    @Configuration 是 Spring 中的一个注解,它用于标记一个类为配置类,通过配置类可以定义和组装 Spring Bean,并且支持高度灵活的配置方式。本问详细介绍了Spring注解 @Configuration,感兴趣的小伙伴可以参考一下
    2023-04-04
  • SpringMVC响应视图和结果视图详解

    SpringMVC响应视图和结果视图详解

    这篇文章主要介绍了SpringMVC响应视图和结果视图,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 一文浅析Java中的值传递

    一文浅析Java中的值传递

    今天在解决一个问题时,程序总是不能输出正确值,分析逻辑思路没问题后,发现原来是由于函数传递导致了这个情况,下面我们就来看看Java中的值传递到底是什么情况吧
    2023-08-08

最新评论