前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mybatis中一级缓存和二级缓存介绍

mybatis中一级缓存和二级缓存介绍

作者头像
小马哥学JAVA
发布2023-07-15 15:46:48
3740
发布2023-07-15 15:46:48
举报
文章被收录于专栏:JAVA开发专栏JAVA开发专栏
一、mybatis中一级缓存和二级缓存有何区别?

mybatis一级缓存的作用域是同一个sqlsession,在同一个sqlsession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写入到缓存(内存),第二次会从缓存中获取,从而提高查询效率。当一个sqlsession结束后该sqlsession中的以及缓存也就不存在了。mybatis默认开启一级缓存。

Mybatis二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace,不同的sqlsession两次执行相同的namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存。

区别:

  1. 一级缓存的作用域是一个sqlsession内,二级缓存作用域是针对mapper进行缓存;
  2. 一级缓存是默认开启的,二级缓存需要手动配置;
二、一级缓存的特点:
下图是根据id查询用户的一级缓存图解

一级缓存区域是根据sqlsession为单位划分的。每次查询会先从缓存区域找,如果找不到从数据库查询,查询到数据将数据写入缓存。mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+sql语句。value为查询出来映射生成的java对象sqlsession执行insert、update、delete等操作的commit提交后会清空缓存区域。

代码语言:javascript
复制
 @Test
    public  void Test(){
        SqlSession sqlSession = MybatisUtils.openSession();
        CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
        Customer customer = customerMapper.getCustomerWithId(2);
        System.out.println("Customer1----"+customer);
        Customer customer2 = customerMapper.getCustomerWithId(2);
        System.out.println("Customer1----"+customer2);
        System.out.println("是否相等:"+(customer2==customer));
        sqlSession.close();
    }

上图显示的命中率为0,是因为我们配置开启了二级缓存,显示的是二级缓存的命中率。查询顺序:二级缓存---》一级缓存---》数据库;

关闭二级缓存的查询结果是:
二级缓存的特点:
开启二级缓存的方式:mybatis主配置文件中加入以下代码:
代码语言:javascript
复制
 <setting name="cacheEnabled" value="true"/>

然后在对于的mapper.xml里面加入配置

代码语言:javascript
复制
<cache eviction="FIFO" flushInterval="6000" readOnly="false" size="50" >

    </cache>
下图对cache的属性进行介绍:

由上图可以看出来,二级缓存是有设置定时情况缓存的时间的,也有设置对应的回收策略,这也很好的解答了上面的疑问(二级缓存什么时候失效)

二级缓存的代码测试:

使用动态代理的方式:

代码语言:javascript
复制
  @Test
    public  void Test2(){
        SqlSession sqlSession = MybatisUtils.openSession();
        CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
        Customer customer = customerMapper.getCustomerWithId(2);
        System.out.println("customer=="+customer);
        sqlSession.close();

        SqlSession sqlSession2= MybatisUtils.openSession();
        CustomerMapper customerMapper2 = sqlSession2.getMapper(CustomerMapper.class);
        Customer customer2 = customerMapper2.getCustomerWithId(2);
        System.out.println("customer2=="+customer2);
        System.out.println("是否相等:"+(customer2==customer));
        sqlSession2.close();
    }
不用动态代理的方式:
代码语言:javascript
复制
@Test
    public  void Test3(){
        SqlSession sqlSession = MybatisUtils.openSession();
        Customer customer = sqlSession.selectOne("getCustomerWithId", 2);
        System.out.println("customer=="+customer);
        sqlSession.close();

        SqlSession sqlSession2= MybatisUtils.openSession();
        Customer customer2 = sqlSession2.selectOne("getCustomerWithId", 2);
        
        System.out.println("customer2=="+customer2);
        System.out.println("是否相等:"+(customer2==customer));
        sqlSession2.close();
    }

运行结果如下图所示:

结果与上图执行结果一样,可以得知缓存与何种方式执行sql无关:

再次执行上面的代码,运行结果直接就异常了:

这个报错的信息是显示实体类必须序列化才行,调整实体类:

再次进行执行结果如下:

运行结果成功,并且只执行了一次sql,第二次执行查询缓存命中,但是查询出来的两个对象却不相等了。

通过设置查询的cache属性上面可以得知,readonly:是否只读,true代表只读,mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。mybatis为了加快获取速度,直接会将数据在缓存中的引用交给用户,不安全,但是速度很快;false:非只读,mybatis觉得获取的数据可能会被修改mybatis会利用序列化或者反序列化的方式克隆一份新的数据给到用户,这样比较安全,但是速度慢。

由于上面说到mybatis会用到序列化反序列化的技术克隆一份新的数据,所以对于的pojo需要实现序列化。并且既然得到的是复制的一份新的数据,所以查询的对象并不相等了,这也可以算法和一级缓存的差别了。

总结:
  1. 一级缓存的作用域是一个sqlsession内;二级缓存的作用域是针对mapper进行缓存的;
  2. 一级缓存默认是开启的;二级缓存需要手动配置;
  3. 一级缓存sqlsession执行insert、update、delete等操作commit提交后悔清空缓存区域。sqlsession.close()后一级缓存也没有了,但是销毁sqlsession后会将里面的缓存存到二级缓存中;
  4. 二级缓存cache中的readonly属性如果为false那么相应的pojo类必须实现serializable接口,并且其缓存查询到的对象都是通过序列化或者反序列化克隆的,所以对象之间两两不相等。
  5. 二级缓存的生命周期和应用同步,它是用来解决一级缓存不能跨会话共享数据的问题,范围是namespace级别的,可以被多个会话共享(只要是同一个接口的相同方法,都可以共享)
  6. 只要没有显示地设置cacheEnable为false,都会使用cacheingexector进行装饰基本的执行器(simple、reuse、batch)二级缓存总是默认开启的,但是每个mapper的二级开关是默认关闭的。
  7. 二级缓存进行增删改操作也会刷新二级缓存,导致二级缓存失效的;
什么情况下会开启二级缓存:
  1. 因为所有的增删改都会刷新二级缓存,导致二级缓存会失效,所以适合在查询为主的应用中使用,比如历史交易、历史订单的查询,否则缓存就失去了意义。
  2. 如果多个namespace中有针对同一个表的操作,比如blog表,如果一个namespace中刷新了缓存,另一个namespace有刷新,就会出现读到脏数据的情况。所以推荐在一个mapper里面只操作单表的情况使用。
本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-04-06,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 小马哥学JAVA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、mybatis中一级缓存和二级缓存有何区别?
  • 二、一级缓存的特点:
    • 下图是根据id查询用户的一级缓存图解
      • 关闭二级缓存的查询结果是:
        • 二级缓存的特点:
          • 开启二级缓存的方式:mybatis主配置文件中加入以下代码:
            • 下图对cache的属性进行介绍:
              • 二级缓存的代码测试:
                • 不用动态代理的方式:
                • 总结:
                • 什么情况下会开启二级缓存:
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
                http://www.vxiaotou.com