ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

MyBatis的一级缓存、二级缓存及脏读

2022-08-03 18:31:47  阅读:147  来源: 互联网

标签:缓存 dicMapper 及脏读 查询 MyBatis selectById Dic id


MyBatis的缓存分为一级缓存和二级缓存,一级缓存默认打开且无法关闭,二级缓存需要手动打开。不管一级缓存还是二级缓存,都存在脏读的情况。

一级缓存支持SqlSession级别,二级缓存能支持到多个SqlSession,且在同一个namespace下面。

 

一级缓存

默认打开,作用域是SqlSession。即同一个SqlSession对同一个查询sql进行查询,会将第一次的查询数据放入到缓存中,而第二次会直接从缓存中拿取,不在查询数据库。

但是当如下情况缓存会失效:

  1. 查询条件不同,即执行的查询sql不同
  2. 同一个事物中执行了增删改操作
  3. 手动清除缓存

测试代码(使用springboot+mybatis):

@Transactional
@Override
public void findById(Serializable id) {
    dicMapper.selectById(id);
    dicMapper.selectById(id);
}

  在springboot中需要开启事物@Transactional才能使一级缓存生效,原因在于需要在同一个事物里。

 

 可以看出两次查询,只发送了一次sql。第二次查询会从缓存中进行查找。

缓存失效

测试代码

@Transactional
@Override
public void findById(Serializable id) {
    Dic dic = dicMapper.selectById(id);
    //执行增删改,使缓存失
dicMapper.updateById(dic); Dic dic2 = dicMapper.selectById(id); }

  结果

 

 二级缓存

需要手动开启,作用域是namespace,支持这个namespace下不同的SqlSession。

 

 

开启步骤:

默认开启了cacheEnabled属性,默认值是true

需要在指定的Mapper上指定开启

// 开启二级缓存
@CacheNamespace
public interface DicMapper extends BaseMapper<Dic> {

且指定的POJO需要实现序列化:

public class Dic implements Serializable

完成以上步骤,就代表在DicMapper 这个namespace下开启了二级缓存。

但是当如下情况缓存会失效:

 1. 在过程中执行了增删改操作

 

测试代码

DicMapper.java
@CacheNamespace
public interface DicMapper extends BaseMapper<Dic> {

    @Select("select * from sys_dic")
    public List<Dic> getAll();

}

 TestController.java

@GetMapping("/allDic")
public Dic getAllDic(){
     List<Dic> all1 = dicMapper.getAll();
     List<Dic> all2 = dicMapper.getAll();
     return null;
}

  输出结果:

 

 对于第二次查询,发现没有走数据库进行查询。

缓存失效

测试代码:

@GetMapping("/allDic")
    public Dic getAllDic(){
        List<Dic> all1 = dicMapper.getAll();
        // 过程中执行增删改操作使缓存失效
        dicMapper.deleteById(298);
        List<Dic> all2 = dicMapper.getAll();
        return null;
    }

输出结果:

 

 缓存下的脏读

虽然MyBatis提供了一级缓存和二级缓存,但是如果在特殊情况下会出现数据读取错误问题,导致读取的数据并不是当下真实的数据。

一级缓存脏读

在同一事物下,使用了不同的SqlSession进行操作时,就会发生脏读;

测试代码:

@Transactional
    public void dirtyData(Serializable id) {
        Dic dic1 = dicMapper.selectById(id);
        System.out.println("第一次查询DIC:\t"+dic1);

        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        DicMapper mapper = sqlSession.getMapper(DicMapper.class);
        Dic dic2 = mapper.selectById(id);
        System.out.println("重新开启SqlSession查询DIC:\t"+dic2);

        dic1.setName(dic1.getName()+"-edit");
        dicMapper.updateById(dic1);
        System.out.println("使用第一次的Mapper进行数据修改");

        Dic dic2_1 = dicMapper.selectById(id);
        System.out.println("使用第一次的Mapper重新查询数据(此刻会查询到正确数据):\t"+dic2_1);

        Dic dic2_2 = mapper.selectById(id);
        System.out.println("使用开启SqlSession重新查询数据(此刻会出现脏读):\t"+dic2_2);
    }

  测试结果:

 

 

二级缓存脏读

使用了不同的Mapper(nameSpace)操作了同一数据对象就会出现脏读

测试代码:

Dic2Mapper.java 创建一个新的Mapper但是也操作Dic对象
@CacheNamespace
public interface Dic2Mapper extends BaseMapper<Dic> {


}

 service层:

public void dirtyDataTwo(Serializable id) {
        Dic dic = dicMapper.selectById(id);
        System.out.println("dicMapper第一次查询:\t"+dic);
        System.out.println("dicMapper2第一次查询:\t"+dicMapper2.selectById(id));
        dic.setName("-tow-edit");
        dicMapper.updateById(dic);
        System.out.println("使用dicMapper进行更新操作");
        Dic dic2 = dicMapper.selectById(id);
        System.out.println("dicMapper第二次查询:\t"+dic2);
        System.out.println("dicMapper2第二次查询:\t"+dicMapper2.selectById(id));
    }

  输出结构:

 

 缓存优先级的使用

从大到小优先执行:

二级缓存 -> 一级缓存 -> 数据库

标签:缓存,dicMapper,及脏读,查询,MyBatis,selectById,Dic,id
来源: https://www.cnblogs.com/swayer/p/16547780.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有