没用好mybatisplus的Wrapper,我真尴尬啊(mybatisplus坑)

  本篇文章为你整理了没用好mybatisplus的Wrapper,我真尴尬啊(mybatisplus坑)的详细内容,包含有mybatisplusupdatewrapper的用法 mybatisplus坑 mybatis plus entitywrapper mybatisplus缺点 没用好mybatisplus的Wrapper,我真尴尬啊,希望能帮助你了解 没用好mybatisplus的Wrapper,我真尴尬啊。

   QueryWrapper/LambdaQueryWrapper/AbstractWrapper/Wrapper... 一图看懂mybatisplus中各个Wrapper类的关系图

  
QueryWrapper/LambdaQueryWrapper/AbstractWrapper/Wrapper... 一图看懂mybatisplus中各个Wrapper类的关系图

  我们的springboot应用程序的持久层,是用jeecgboot框架生成的代码。其中,mybatisplus版本是3.1.2。

  

  在一次对交易数据的分页查询代码做性能优化时,我在Mapper里重写了父接口BaseMapper的selectPage方法。其中,调用Wrapper T 参数对象的between操作,为最终的sql加上了id区间限制,以提高sql执行性能。

  

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

 

  public interface SbhPlatOrderMapper extends BaseMapper SbhPlatOrder {

   @Override

   default IPage SbhPlatOrder selectPage(IPage SbhPlatOrder page, @Param(Constants.WRAPPER) Wrapper SbhPlatOrder queryWrapper){

   PrePageDto prePageDto = selectCountCache(queryWrapper);

   page.setTotal(prePageDto.getRowCount());

   if (prePageDto.getRowCount() 0) {

   if (queryWrapper instanceof LambdaQueryWrapper) {

   ((LambdaQueryWrapper SbhPlatOrder ) queryWrapper).between(SbhPlatOrder::getId, prePageDto.getMinId(), prePageDto.getMaxId());

   } else if (queryWrapper instanceof QueryWrapper) {

   ((QueryWrapper SbhPlatOrder ) queryWrapper).lambda().between(SbhPlatOrder::getId, prePageDto.getMinId(), prePageDto.getMaxId());

   page.setRecords(selectPageList((page.getCurrent() - 1) * page.getSize(), page.getSize(), queryWrapper));

   return page;

   @Cacheable(cacheNames = RedisConfig.SBH_PLAT_ORDER_COUNT_CACHE_KEY,

   key = "T(com.emax.zhenghe.common.util.security.MD5Util).md5Encode(#queryWrapper.customSqlSegment)")

   PrePageDto selectCountCache(@Param(Constants.WRAPPER) Wrapper SbhPlatOrder queryWrapper);

   List SbhPlatOrder selectPageList(long offset, long pageSize, @Param(Constants.WRAPPER) Wrapper SbhPlatOrder queryWrapper);

  }

 

  

  程序上线后运行了一段时间。后来,发现了一个问题。见下面日志截图

  

  原来呢,在业务service类中调用这个分页的方法里,并不是每次调用分页时都new一个Wrapper对象,而是重复使用同一个Wrapper对象。见下图。 所以,出现上图的问题就不难理解了。

  

  解决办法 -Wrapper#getCustomSqlSegment

  头疼医头的方式,是修改service类里调用这个分页的方法,每次调用分页前都new一个Wrapper对象。

  显然,这样解决问题只是一时。以后再有调用这个分页的地方,依然会存在这个问题呀。

  所以,我的解决办法是利用 Wrapper#getCustomSqlSegment, 上面sql里重复出现的是 “id BETWEEN”。所以,修正的代码如下:

  

 // 外面调用处可能复用这个queryWrapper对象。所以,为避免重复追加条件,这里先做判读再追加。

 

   if (!queryWrapper.getCustomSqlSegment().contains("id BETWEEN")) {

   if (queryWrapper instanceof LambdaQueryWrapper) {

   ((LambdaQueryWrapper SbhPlatOrder ) queryWrapper).between(SbhPlatOrder::getId, prePageDto.getMinId(), prePageDto.getMaxId());

   } else if (queryWrapper instanceof QueryWrapper) {

   ((QueryWrapper SbhPlatOrder ) queryWrapper).lambda().between(SbhPlatOrder::getId, prePageDto.getMinId(), prePageDto.getMaxId());

   }

 

  

  更好的解决办法-对象克隆

  上面的解决办法存在两个瑕疵:①使用了字符串contains操作,不利于维护,如果日后id字段重命名,而忽略了这里的修改,就出现bug;②限定了只调用一次between操作,而在某些情况下maxId/minId可能是会发生变化的,这样就又会出现隐患。mybatisplus的Wrapper也不提供移除某个已经存在的条件的操作api。

  我注意到,QueryWrapper和LambdaQueryWrapper的clone方法。经测试,可行。

  下面源码,是mybatisplus的抽象类AbstractWrapper,重写的超类Object#clone。

  

package com.baomidou.mybatisplus.core.conditions;

 

  public abstract class AbstractWrapper ...{

   @Override

   @SuppressWarnings("all")

   public Children clone() {

   return SerializationUtils.clone(typedThis);

  }

 

  如此一来,上面的selectPage方法就可以进一步改造一番了。写这篇博文是子夜11:20,我即将关机休息。三下五除二,我就在这篇文章的文本编辑器里写下面的selectPage代码:

  

 @Override

 

   default IPage SbhPlatOrder selectPage(IPage SbhPlatOrder page, @Param(Constants.WRAPPER) Wrapper SbhPlatOrder queryWrapper){

   Wrapper SbhPlatOrder queryWrapperClone = queryWrapper.clone();

   // 下面的代码同上,只不过都访问的是queryWrapperClone ,而不再是queryWrapper。

   }

 

  第二天上班后,我开始沿着上面的思路改工程里的代码。却发现行不通!

  经过梳理才发现,原来mybatisplus中,各种Wrapper的关系是下面这样子滴

  

  这个类关系图传递如下信息:

  QueryWrapper与LambdaQueryWrapper 两者本身不存在继承关系。而是都继承了AbstractWrapper。

  QueryWrapper和LambdaQueryWrapper 与 他们的抽象父类AbstractWrapper 的泛型不同,AbstractWrapper类有3个泛型参数。 所以,试图将上面selectPage方法的第二个参数类型由Wrapper改为子类AbstractWrapper,然后再在方法第一行调用其clone方法是不可以的。

  

  结合起来,将上面selectPage方法代码做如下改动:

  

if (prePageDto.getRowCount() 0) {

 

   // 外面调用处可能复用这个queryWrapper对象。所以,为避免重复调用between等操作追加条件,做如下处理

   if (queryWrapper instanceof LambdaQueryWrapper) {

   LambdaQueryWrapper SbhPlatOrder clone = ((LambdaQueryWrapper SbhPlatOrder ) queryWrapper).clone();

   clone.between(SbhPlatOrder::getId, prePageDto.getMinId(), prePageDto.getMaxId());

   page.setRecords(selectPageList((page.getCurrent() - 1) * page.getSize(), page.getSize(), clone));

   } else if (queryWrapper instanceof QueryWrapper) {

   QueryWrapper SbhPlatOrder clone = ((QueryWrapper SbhPlatOrder ) queryWrapper).clone();

   clone.lambda().between(SbhPlatOrder::getId, prePageDto.getMinId(), prePageDto.getMaxId());

   page.setRecords(selectPageList((page.getCurrent() - 1) * page.getSize(), page.getSize(), clone));

  }

 

  

  EOF-thanks for reading.

  
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自,转载请注明原文链接:https:///buguge/p/16940068.html

  以上就是没用好mybatisplus的Wrapper,我真尴尬啊(mybatisplus坑)的详细内容,想要了解更多 没用好mybatisplus的Wrapper,我真尴尬啊的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: