Mybatis二次深度学习整理——缓存、插件

本文详细探讨了Mybatis的一级和二级缓存,解析了它们的工作原理、应用场景及实现方式。一级缓存是SqlSession级别的,而二级缓存是Mapper级别的,可以跨Sqlsession使用。二级缓存可以通过Redis实现,以适应分布式架构的需求。此外,文章还介绍了Mybatis的插件机制,包括插件原理、自定义插件以及常用的分页和通用Mapper插件的使用。
摘要由CSDN通过智能技术生成

Mybatis缓存

缓存就是存储在内存中的数据,内存和硬盘的区别想必不陌生,主要是体现在读写速度价格上不是一个级别的,为了防止高并发的场景下,硬盘无法支持IO性能要求,所以就有了将数据储存在内存中的概念,也就是缓存了,使用缓存可以避免频繁与数据库交互,进而提高响应速度。

Mybatis是分为一级缓存和二级缓存的

  • 一级缓存是Sqlsession级别的缓存,在操作数据库的时候需要构造SqlSession对象,在对象中会有一个HashMap用于存储缓存的数据,不同的Sqlsession是互不干扰的。
  • 二级缓存是Mapeer级别的缓存,多个Sqlsession操作同一个Mapeer,也就是namespace,是可以同享二级缓存的。

一级缓存

简单应用

一级缓存是默认开启的
先看一下代码,测试一下一级缓存的应用,主要是以两次查询来测试的

    @Test
    public void oneCache(){
   
        SqlSession sqlSession = this.factory.openSession();
        OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
        List<Order> orders1 = orderMapper.selectById(1);
        System.out.println(Arrays.toString(orders1.toArray()));
        List<Order> orders2 = orderMapper.selectById(1);
        System.out.println(Arrays.toString(orders2.toArray()));
        // 一级缓存 缓存的是查询出的对象引用
        System.out.println(orders1 == orders2);
    }

控制台打印结果

可以看到只发生了一次向数据库查询数据,并且第一次查询和第二次查询他们的对象是相同的。这说明第一次的时候,在缓存中并没有响应的数据,所以先查询了数据库,第二次的时候因为已经存在的缓存中,所以第二次查询没有查询数据库,而是直接在缓存中拿到了数据。

再看一个刷新一级缓存的情况

    @Test
    public void oneCacheFlush(){
   
        SqlSession sqlSession = this.factory.openSession();
        OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
        List<Order> orders1 = orderMapper.selectById(1);
        System.out.println(Arrays.toString(orders1.toArray()));
        // 更新数据或者手动刷新都会导致一级缓存刷新
//        Order order = new Order();
//        order.setId(1);
//        order.setTotal(new BigDecimal(50));
//        orderMapper.updateOrder(order);
//        sqlSession.commit();
        sqlSession.clearCache();
        List<Order> orders2 = orderMapper.selectById(1);
        System.out.println(Arrays.toString(orders2.toArray()));
        // 一级缓存 缓存的是查询出的对象引用
        System.out.println(orders1 == orders2);
    }

控制台

这是因为在第一次查询后,紧接着修改了数据或者手动刷新了一级缓存,导致在一级缓存中相应的缓存数据清空,所以第二次查询依然查询了数据库,增删改都会清空响应缓存数据,只要是为了防止查询时出现脏读。
流程图如下所示

原理剖析

下面带着几个问题看下面的内容,一级缓存是什么?一级缓存在什么时候被创建的?它的具体工作流程又是什么?

一级缓存是什么?

关于这个问题,其实在上面的内容中已经有了解答,它就是一个HashMap,并且它是和SqlSession相关联的HashMap,所以它的作用域只能是SqlSession范围的,其他Sqlsession是无法使用的。
那如何找到这个HashMap的具体所在位置呢?
可以看到上面的代码中,有一个手动清空一级缓存的操作,sqlSession.clearCache();,就可以以此入手看一下,到底如何清空缓存的,那么自然也就找到了这个缓存了

如图所示,在BaseExecutor中有一个属性是PerpetualCache,这个就是Mybatis中内置的一级缓存对象

如图所示,这个就是这个对象如何是缓存数据以及清理数据的,可以看到最终保存数据的就是一个HashMap。

一级缓存在什么时候被创建的?

分析这个问题的时候,可以参考之前的一张图

从这张图中就可以很清晰的了解,是在第一次查询的时候进行缓存数据的,也就是在查询的时候,那么有了相应的方向,可以看一下Executor中如何创建的。为什么是Executor,主要是在Mybatis中Executor主要就是执行Sql和数据库打交道的地方

可以在BaseExecutor中找到这个createCacheKey方法,就是通过这个方法去创建刚刚存储缓存的Key的,可以看一下他的Key是由几个参数组成的呢?
分别是StatementId、rowBounds、boundSql、value(param)、environmentId,一共是这五部分组成,他们分别代表

  • StatementId: namespace+具体语句的Id
  • rowBounds: 分页参数,假如没有设置分页参数,默认是Offset=0,Limit = Integer.MAX_VALUE
  • boundSql: 具体查询Sql
  • value(param): 查询时所涉及的参数
  • environmentId: 这个主要是指Mybatis配置文件中的环境Id

如图,很直观了:

它的具体工作流程又是什么?

最后一个问题,他的工作流程是什么?
可以直接由createCacheKey这个方法出发,看看他的调用方法

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
   
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 创建缓存标识
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }
@SuppressWarnings("unchecked")
  @Override
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值