mybatis的缓存机制

简介: 🍅 Java学习路线:搬砖工的Java学习路线🍅 作者:程序员小王🍅 程序员小王的博客:https://www.wolai.com/wnaghengjie/ahNwvAUPG2Hb1Sy7Z8waaF🍅 扫描主页左侧二维码,加我微信 一起学习、一起进步🍅 欢迎点赞 👍 收藏 ⭐留言 📝

mybatis的缓存机制

一、缓存:缓存存储

1、现有的查询策略:

1.png


现有查询策略:每次查询都会链接访问数据库
存在的问题:每次都需要获取链接,释放连接资源,降低程序运行效率,解决方案使用缓存


2、使用缓存:

2.png


缓存:内存中的一块存储空间,用于存放多个用户反复查询数据,有了缓存之后
后续的查询数据都会直接从缓存中获取

3、使用缓存的好处:

减少每次使用链接的占用,提升查询效率,
提升程序的运行效率


4、使用缓存的缺点:

占用大量的内存资源(成本高)

5、缓存的机制:

以空间换时间(效率)


二、mybatis缓存

1、mybatis的缓存机制

(1)一级缓存 基于sqlSession的缓存【默认开启】

一级缓存:也称为本地缓存,用于保存用户在一次会话过程中查询的结果,用户一次会话中只能使用一个sqlSession,一级缓存是自动开启的,不允许关闭。

注意:查询语句最后都是关闭资源,如果不关闭资源数据会出现闪动


不关闭资源,第一次查询四条,第二次5条,SqlSession.close()关闭资源就是为了关闭sqlSession的缓存


一级缓存的特点:


1、查询第一次时,获取到数据写入一级缓存,再次查询时从缓存获取,不再执行sql语句
2、若当前SqlSession发生修改、增加、删除动作时,就会立即把当前缓存的所有数据清空
3、对于查询操作,只要SqlSession没有调用flush或者close方法,它就一直存在


注意:一次缓存造成查询数据结果闪动

3.png


(2)二级缓存:基于mapper的缓存(基于指定实体类的缓存)(末默认不开启)

二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。(也可以说是,在同一namespace下,共享一块缓存空间,如果多个mapper (dao.xml)共享同一namesapce 则也共享一块缓存,二级缓存是跨sqlsession,多个sqlsession可以去二级缓存获取数据。即可以针对同一个dao接口或者同一个命名空间(namespace)创建多个SqlSession )


二级缓存特点:

1、只要发生增删改,就会将·同一命名空间(namespace)下的缓存清空
2、使用二级缓存实体类必须实现序列化,否则报错
3、使用查询语句,默认只写入一级缓存,只有调用close(),commit()方法,
   才会将数据提交到二级缓存,其他的sqlsession才能拿到,不再执行sql语句

2、二级缓存 开启手工设置

1、在Mybatis框架中声明 使用二级缓存 在mybatis-config.xml中配置

mybatis-config.xml中

mybatis-config.xml:
<!--    设置缓存-->
    <settings>
        <!--开启Mybatis二级缓存
             cacheEnabled:启动mybatis二级缓存   值:布尔值   false:不启用  【默认值】   true :开启
         -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

     

注意:

4.png

2、在需要进行缓存的操作Mapper中指定使用缓存 在mapper文件中声明

  <!--开启当前查询操作缓存-->
    <cache></cache>

 

3、要缓存的实体类必须实现序列化接口(实体类中)

public class User implements Serializable {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private Date birthday;
  • 实体类实现序列化接口的原因:

数据的数据唤出:长时间不用的放到缓存文件里

5.png


数据的唤入:当再次使用时,

6.png


4、缓存测试

1、在mybatis工具类中重写一个执行sqlSession的方法:

作用:用于测试缓存,实际开发不使用

//mybatisUtil.java 工具类
public class MybatisUtil {
    /**
     * 线程绑定对象,保证是同一个对象
     */
    private static final ThreadLocal<SqlSession> t1 = new ThreadLocal<>();
    private static SqlSessionFactory factory;
    static {
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        factory = new SqlSessionFactoryBuilder().build(is);
    }
    /**
     * 创建SqlSession
     *
     * @return sqlSession
     */
    private static SqlSession openSession() {
        SqlSession sqlSession = t1.get();
        if (sqlSession == null) {
            sqlSession = factory.openSession();
            t1.set(sqlSession);
        }
        return sqlSession;
    }
    /**
     * 仅用于测试缓存  每次调用方法获取的都是全新的连接  实际开发不使用
     * @return sqlSession
     */
    public static SqlSession getSqlSession(){
        return factory.openSession();
    }
    /**
     * 调用功能方法
     *
     * @param clazz
     * @return Object
     */
public  static Object getMapper(Class clazz) {
        return openSession().getMapper(clazz);
    }
    /**
     * 关闭资源
     */
    public static void close() {
        if (openSession() != null) {
            openSession().close();
        }
        t1.remove();
    }
    /**
     * 提交事务,关闭资源
     */
    public static void commit() {
        //1、提交事务
        commit();
        //2、关闭资源
        close();
        t1.remove();
    }
    /**
     * 回滚事务,关闭资源
     */
    public static void rollback() {
        //1、提交事务
        openSession().rollback();
        //2、关闭资源
        close();
        t1.remove();
    }
}

工具类的sqlSession不开启线程绑定对象【这样每次查询都会创建一个sqlSession】

 

    /**
     * 仅用于测试缓存  每次调用方法获取的都是全新的连接  实际开发不使用
     * @return sqlSession
     */
    public static SqlSession getSqlSession(){
        return factory.openSession();
    }


7.png

7.png


2、测试


//注意:测试之前mybatis工具类调用的方法不能开启ThreadLocal线程绑定对象,否则调用的都是同一个sqlSession
//必须关闭链接,不关闭放在一级缓存里,关掉才会放在二级缓存区
@Test
    public void CacheTest() {
        //第一次id查询id为1的同学
        SqlSession sqlSession1 = MybatisUtil.getSqlSession();
        UserDao mapper = sqlSession1.getMapper(UserDao.class);
        User user = new User();
        user.setId(1);
        List<User> users = mapper.selectUserAll(user);
        System.out.println("第一次id查询id为1的同学:");
        for (User user1 : users) {
            System.out.println(user1);
        }
        //必须关闭链接,不关闭放在一级缓存里,关掉才会放在二级缓存
        MybatisUtil.close();
        //第二次id查询id为1的同学
        SqlSession sqlSession2 = MybatisUtil.getSqlSession();
        UserDao mapper2 = sqlSession1.getMapper(UserDao.class);
        User user2 = new User();
        user.setId(1);
        List<User> users2 = mapper.selectUserAll(user);
        System.out.println("第二次id查询id为1的同学:");
        for (User user3: users2) {
            System.out.println(user3);
        }
        //必须关闭链接,不关闭放在一级缓存里,关掉才会放在二级缓存
        MybatisUtil.close();
    }


3、注意事项:


1、注意:测试之前mybatis工具类调用的方法不能开启ThreadLocal线程绑定对象,
        否则调用的都是同一个sqlSession
2、**必须关闭链接,不关闭放在一级缓存里,关掉才会放在二级缓存区中** 

3、二级缓存执行机制

开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示。


8.png

二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。

当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。


1、一级缓存与二级缓存调用顺序:

获取数据先去二级缓存获取,如果有得到,写入(更新)一级缓存**
若没有得到,再去一级缓存,如果仍没有,再去数据库**
优先级: 二级缓存--->一级缓存--->数据库**


三、缓存中的脏数据

1、出现脏数据的原因:

1、主要原因:
数据库增删改之后,缓存的数据和数据库中的数据不匹配
2、详细原因:
使用二级缓存,当有一个session发生修改(增删改)时,
将二级缓存清空了,然而另一个session缓存过以前的查询结果
(此时二级缓存没数据,去一级缓存却拿到了之前的),则可能产生脏数据。


9.png


如上图:


1、第一步sqlsession1 去查询id=1的数据,
  并写入了sqlsession1的一级缓存与共同的二级缓存
2、第二步sqlsession2 去查询id=1的数据,
   发现二级缓存有数据,就不执行sql,并将其写入sqlsession2的一级缓存
3、第三步sqlsession2执行**修改** id=1的数据,
   清空了二级缓存与sqlsession2的一级缓存
4、第四步sqlsession1 再去查询id=1的数据,二级缓存没有,
   但却从sqlsession1的一级缓存读到了数据,但这却是修改之前的,就产生了脏数据

2、脏数据解决方案

判断是否刷新缓存flushCache;在默认的设置的select语句是不会刷新缓存的,insert/update/delte会刷新缓存。

3、Mybatis脏数据解决:

根据是否控制事务来判断是否刷新(清空)缓存:

SqlSession.commit()
SqlSession.rollback()

注意:

1、Mybatis会在事务控制时清空缓存,

2、后续功能查询一定不要控制事务(清空缓存) ,

3、增删改一定要控制事务


文末

👉在此,鸣谢:刘浩老师讲解

📌 作者:王恒杰

❌ 勘误: 无

📜 声明: 由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!

🍅 欢迎点赞 👍 收藏 ⭐留言 📝


相关文章
|
2月前
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
3月前
|
缓存 应用服务中间件 nginx
Web服务器的缓存机制与内容分发网络(CDN)
【8月更文第28天】随着互联网应用的发展,用户对网站响应速度的要求越来越高。为了提升用户体验,Web服务器通常会采用多种技术手段来优化页面加载速度,其中最重要的两种技术就是缓存机制和内容分发网络(CDN)。本文将深入探讨这两种技术的工作原理及其实现方法,并通过具体的代码示例加以说明。
338 1
|
6天前
|
SQL Java 数据库连接
Mybatis架构原理和机制,图文详解版,超详细!
MyBatis 是 Java 生态中非常著名的一款 ORM 框架,在一线互联网大厂中应用广泛,Mybatis已经成为了一个必会框架。本文详细解析了MyBatis的架构原理与机制,帮助读者全面提升对MyBatis的理解和应用能力。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Mybatis架构原理和机制,图文详解版,超详细!
|
15天前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
15天前
|
SQL 缓存 Java
MyBatis如何关闭一级缓存(分注解和xml两种方式)
MyBatis如何关闭一级缓存(分注解和xml两种方式)
43 5
|
1月前
|
缓存 Java 数据库连接
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持自定义 SQL 执行、映射及复杂查询。本文介绍了如何在 Spring Boot 项目中集成 MyBatis 并实现一级和二级缓存,以提高查询性能,减少数据库访问。通过具体的电商系统案例,详细讲解了项目搭建、缓存配置、实体类创建、Mapper 编写、Service 层实现及缓存测试等步骤。
|
1月前
|
存储 缓存 负载均衡
Nginx代理缓存机制
【10月更文挑战第2天】
70 4
|
1月前
|
存储 缓存 NoSQL
深入理解后端缓存机制的重要性与实践
本文将探讨在后端开发中缓存机制的应用及其重要性。缓存,作为提高系统性能和用户体验的关键技术,对于后端开发来说至关重要。通过减少数据库访问次数和缩短响应时间,缓存可以显著提升应用程序的性能。本文将从缓存的基本概念入手,介绍常见的缓存策略和实现方式,并通过实例展示如何在后端开发中有效应用缓存技术。最后,我们将讨论缓存带来的一些挑战及其解决方案,帮助您在实际项目中更好地利用缓存机制。
|
2月前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
67 8