广告
返回顶部
首页 > 资讯 > 后端开发 > Python >mybatis-plus如何禁用一级缓存的方法
  • 306
分享到

mybatis-plus如何禁用一级缓存的方法

2024-04-02 19:04:59 306人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

前言 用过mybatis-plus的朋友可能会知道,mybatis-plus提供了多租户插件的功能,这个功能可以让开发人员不用手动写租户语句,由该插件自动帮你加上租户语句。今天的素

前言

用过mybatis-plus的朋友可能会知道,mybatis-plus提供了多租户插件的功能,这个功能可以让开发人员不用手动写租户语句,由该插件自动帮你加上租户语句。今天的素材来源就是取自业务开发人员使用多租户插件时,遇到的一个神奇的问题

问题重现

业务开发人员要实现根据手机号码更新租户的密码功能,其代码形如下


 for(Tenant t : tenantList){
      ApplicationChainContext.getCurrentContext().put(ApplicationChainContext.TENANT_ID,t.getId()+"");
      Optional<SaasUser> user = this.findByUserPhone(req.getUserPhone());
      user.ifPresent(u -> {
        count.getAndSet(count.get() + 1);
        LambdaUpdateWrapper<SaasUser> wrapper = new LambdaUpdateWrapper<>();
        String md5Pwd = Md5Utils.hash(req.getNewUserPwd());
        wrapper.eq(SaasUser::getId,user.get().getId());
        wrapper.set(SaasUser::getUserPwd,md5Pwd);
        this.update(wrapper);
      });
    }

从代码上看起来没啥问题,因为使用了多租户插件,当我们执行this.findByUserPhone(req.getUserPhone());就会自动带上租户的信息。但在执行的时候发现一个问题,如下图

从图中我们可以发现,当查询不到用户信息时,后续的查询操作,都没有sql语句出现。这说明2点,sql语句要么被系统吃了,要么系统没有执行sql

问题分析

前面说了sql语句没有打印出来,说明sql要么没执行,要么就sql语句被系统吃了。到底是哪种,与其猜测,倒不如去官方找找问题的答案,很遗憾在mybatis-plus官网或者issue上并没找到答案,于是只好跟踪源码进行分析。最后发现mybatis-plus果然如他官网介绍的只做增强不做改变,他最终调用查询的逻辑,走的是原生mybatis的查询逻辑。其查询的核心代码如下


public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (this.closed) {
      throw new ExecutorException("Executor was closed.");
    } else {
      if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
        this.clearLocalCache();
      }

      List list;
      try {
        ++this.queryStack;
        list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
        if (list != null) {
          this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
        } else {
          list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
      } finally {
        --this.queryStack;
      }

      if (this.queryStack == 0) {
        Iterator var8 = this.deferredLoads.iterator();

        while(var8.hasNext()) {
          BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
          deferredLoad.load();
        }

        this.deferredLoads.clear();
        if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
          this.clearLocalCache();
        }
      }

      return list;
    }
  }

从代码我们可以得出一个重要的信息,如下


 list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
        if (list != null) {
          this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
        } else {
          list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }

当resultHandler为空 ,list的取值是this.localCache.getObject(key),即会走本地缓存,而不会进行数据库查询

问题破解

从源码可以得知,原生的mybatis默认会走本地缓存,即所谓的一级缓存,而mybatis-plus作为mybatis的增强版,其逻辑和mybatis原生逻辑是一样的。那如何禁用mybatis-plus的一级缓存呢,从源码分析,我们可以得知,当list为空时,则不会走缓存,而会查询数据。而list的缓存取值,来源于
this.localCache.getObject(key)。因此禁用缓存的逆向思维就是要么清空localCache,要么就是变更key,使this.localCache.getObject(key)取到的值为null。因此解决方案至少有以下两种

方案一:清空localCache

那要怎么清空?

通过源码我们可以看到清空localCache的地方有两处,一处是


if (queryStack == 0 && ms.isFlushCacheRequired()) {
   clearLocalCache();
  }

另外一处是


if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
    // issue #482
    clearLocalCache();
   }

因此我们可以通过变更configuration.getLocalCacheScope()为STATEMENT进行清空。可以通过在yml做如下配置


mybatis-plus:
  configuration:
    local-cache-scope: statement

方案二:变更localcache的key,使this.localCache.getObject(key)取到的值为null

我们先看下key的构成


public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (this.closed) {
      throw new ExecutorException("Executor was closed.");
    } else {
      CacheKey cacheKey = new CacheKey();
      cacheKey.update(ms.getId());
      cacheKey.update(rowBounds.getOffset());
      cacheKey.update(rowBounds.getLimit());
      cacheKey.update(boundSql.getSql());
      List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
      TypeHandlerReGIStry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
      Iterator var8 = parameterMappings.iterator();

      while(var8.hasNext()) {
        ParameterMapping parameterMapping = (ParameterMapping)var8.next();
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          String propertyName = parameterMapping.getProperty();
          Object value;
          if (boundSql.hasAdditionalParameter(propertyName)) {
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }

          cacheKey.update(value);
        }
      }

      if (this.configuration.getEnvironment() != null) {
        cacheKey.update(this.configuration.getEnvironment().getId());
      }

      return cacheKey;
    }
  }

从源码可以看出key是由statementId+原生sql+value(查询出来的对象)+ sqlsession.hashcode组成。

因此变更key,我们可以从原生sql入手。看到这边可能有朋友会说,租户id正常不都不一样吗,既然mybatis-plus通过多租户插件,其产生的sql语句不都不一样吗,进而产生的key不就都不一样了吗。如果跟踪源码就会发现其原生的sql是没有加上租户信息的。因此我们可以取巧在查询的sql语句中添加一个随机数,形如下


 public Optional<SaasUser> findByUserPhone(String userPhone) {
    LambdaQueryWrapper<SaasUser> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(SaasUser::getUserPhone,userPhone);
    wrapper.apply("{0} = {0}",UUID.randomUUID().toString());
    SaasUser saasUser = getBaseMapper().selectOne(wrapper);
    return Optional.ofNullable(saasUser);
  }

即在原先的查询代码语句多加


wrapper.apply("{0} = {0}",UUID.randomUUID().toString());

此时sql语句如下

 Preparing: SELECT id, user_code, user_name, main_accout_flag, user_pwd, user_phone, admin_flag, user_status, last_login_time, login_ip, pwd_update_time, tenant_id, create_date, created_by, created_by_id, last_updated_by, last_updated_by_id, last_update_date, object_version_number, delete_flag FROM saas_user WHERE delete_flag = 0 AND (user_phone = ? AND ? = ?) AND tenant_id = 424210194470490118
==> Parameters: 111111(String), edcda7fe-ee43-481a-90f7-8b41cb51a3D1(String), edcda7fe-ee43-481a-90f7-8b41cb51a3d1(String)

这样每次产生的sql就会不一样,导致取到不一样key,进而使this.localCache.getObject(key)为空,这样就可以让mybatis每次都进行数据库查询,从而达到禁用一级缓存的目的

总结

方案一的配置是基于全局配置,方案二是基于局部配置。就个人而言,是比较推荐方案二,即通过添加随机值的方式。因为mybatis配置一级缓存的意义,本身就是出于提供性能考虑。不过方案要站在业务的视角进行考虑,为了确保功能能正确运行,有时候牺牲一些性能也无伤大雅

到此这篇关于mybatis-plus如何禁用一级缓存的方法的文章就介绍到这了,更多相关mybatis-plus 禁用一级缓存内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: mybatis-plus如何禁用一级缓存的方法

本文链接: https://www.lsjlt.com/news/121820.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • mybatis-plus如何禁用一级缓存的方法
    前言 用过mybatis-plus的朋友可能会知道,mybatis-plus提供了多租户插件的功能,这个功能可以让开发人员不用手动写租户语句,由该插件自动帮你加上租户语句。今天的素...
    99+
    2022-11-11
  • 使用MyBatis如何实现一级缓存与二级缓存
    这期内容当中小编将会给大家带来有关使用MyBatis如何实现一级缓存与二级缓存,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。MyBatis缓存我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB...
    99+
    2023-05-31
    mybatis 一级缓存 二级缓存
  • Mybatis的一级缓存和二级缓存原理分析与使用
    目录Mybatis的一级缓存和二级缓存1 Mybatis如何判断两次查询是完全相同的查询2 二级缓存2.1 二级缓存配置2.2 二级缓存特点2.3 配置二级缓存2.4 测试Mybat...
    99+
    2022-11-12
  • 如何利用Redis作为Mybatis的二级缓存
    目录前言要优雅就选择MyBATis-PlusRedis配置自定义Mybatis缓存测试缓存命中率(Cache Hit Ratio)一级缓存和二级缓存什么时候该开启二级缓存前言 今天在开发时发现一个奇怪的问题,我手动改完数...
    99+
    2022-08-11
    利用Redis作为Mybatis二级缓存 RedisMybatis二级缓存
  • SpringBoot+Mybatis项目中如何使用Redis做Mybatis的二级缓存
    这篇文章给大家分享的是有关SpringBoot+Mybatis项目中如何使用Redis做Mybatis的二级缓存的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。介绍使用mybatis时可以使用二级缓存提高查询速度,...
    99+
    2023-05-30
    springboot redis mybatis
  • MyBatis-Plus中如何使用ResultMap的方法示例
    目录问题说明解决方法自定义@AutoResultMap注解MyBatis-Plus (简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、...
    99+
    2022-11-12
  • 使用Mybatis如何实现配置二级缓存
    这篇文章给大家介绍使用Mybatis如何实现配置二级缓存,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Mybatis的二级缓存配置相当容易,要开启二级缓存,只需要在你的Mapper 映射文件中添加一行:<...
    99+
    2023-05-31
    mybatis 二级缓存
  • 浅谈Mybatis Plus的BaseMapper的方法是如何注入的
    目录Mybatis Plus的BaseMapper的方法Mybatis Plus的初始化方法MybatisPlusAutoConfiguration中的SqlSessionFacto...
    99+
    2022-11-12
  • Android如何清除应用缓存的方法
    本篇内容主要讲解“Android如何清除应用缓存的方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Android如何清除应用缓存的方法”吧!第一种使用ActivityManager中的clea...
    99+
    2023-06-14
  • 在已经使用mybatis的项目里引入mybatis-plus结果不能共存如何解决
    本篇内容介绍了“在已经使用mybatis的项目里引入mybatis-plus结果不能共存如何解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成...
    99+
    2023-07-05
  • Python 日志系统的高级技巧:缓存 API 的使用方法。
    Python 日志系统的高级技巧:缓存 API 的使用方法 在现代软件开发中,日志系统是一个不可或缺的部分。它可以帮助开发者快速定位问题,排除错误,提高应用程序的稳定性和可靠性。Python 的日志系统是一个强大的工具,可以方便地记录各种级...
    99+
    2023-08-25
    日志 缓存 api
  • 缓存调用链如何实现JS方法的重载
    小编给大家分享一下缓存调用链如何实现JS方法的重载,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.什么是方法重载方法重载是指在...
    99+
    2022-10-19
  • Mybatis-plus如何提前获取实体类用雪花算法生成的ID
    Mybatis-plus中,通过设置@TableId可以让Mybatis-plus自动为我们生成雪花算法的ID号,该ID号是一个长整型数据,非常方便。但是雪花算法的ID号是在Inse...
    99+
    2022-11-13
  • PHP中使用缓存数组索引是一种高效的方法吗?
    PHP是一种流行的编程语言,而缓存数组索引是一种常见的优化技术。但是,缓存数组索引是否真的是一种高效的方法呢?本文将深入探讨这个问题,并演示如何在PHP中使用缓存数组索引。 什么是缓存数组索引? 在PHP中,数组是一种非常常见的数据类型。它...
    99+
    2023-08-06
    缓存 数组 索引
  • Python如何手动编写一个自己的LRU缓存装饰器的方法实现
    LRU缓存算法,指的是近期最少使用算法,大体逻辑就是淘汰最长时间没有用的那个缓存,这里我们使用有序字典,来实现自己的LRU缓存算法,并将其包装成一个装饰器。 1、首先创建一个my_c...
    99+
    2022-11-12
  • Java8如何使用Map中computeIfAbsent方法构建本地缓存
    这篇文章将为大家详细讲解有关Java8如何使用Map中computeIfAbsent方法构建本地缓存,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、概念及使用介绍在JAVA8的Map接口中,增加了一个方...
    99+
    2023-06-17
  • Win8系统触控手势如何禁用 Win8系统禁用触控手势的方法
    Win8系统触控手势如何禁用?触控手势是笔记本独有的功能,很多用户可能不太喜欢,因为偶尔会触碰到,那么怎么禁用触控手势?其实很简单,如果你不懂Win8系统怎么禁用触控手势的话,那就赶紧看看小编提供的教程内容吧! 适用范围...
    99+
    2023-05-22
    笔记本触控板手势 触控板手势 Win8系统禁用触控手势
  • 如果20万用户同时访问一个热点缓存,如何优化你的缓存架构?
    目录(1)为什么要用缓存集群(2)20万用户同时访问一个热点缓存的问题(3)基于流式计算技术的缓存热点自动发现(4)热点缓存自动加载为JVM本地缓存(5)限流熔断保护(6)总结(1)为什么要用缓存集群这篇文章,咱们来聊聊热点缓存的架构优化问...
    99+
    2023-06-05
  • 如何解决mybatis中方法返回泛型与resultType不一致的问题
    这篇文章主要介绍“如何解决mybatis中方法返回泛型与resultType不一致的问题”,在日常操作中,相信很多人在如何解决mybatis中方法返回泛型与resultType不一致的问题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的...
    99+
    2023-06-20
  • 如何在mysql进行查询缓存及失败的解决方法
    都知道函数在使用前需要弄清楚参数的属性,这样才能对函数的使用有较好的了解。有些小伙伴学习了查询缓存后,直接进行了下一步的实战操作。这里小编想提醒大家,开始操作之前一定要先设置参数,不然就会出现问题。下面我们来完整的讲...
    99+
    2022-05-26
    mysql 查询缓存
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作