栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

如何解决mybatis-plus调用update方法时,自动填充字段不生效问题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何解决mybatis-plus调用update方法时,自动填充字段不生效问题

前言

使用过mybatis-plus的朋友可能会知道,通过实现元对象处理器接口com.baomidou.mybatisplus.core.handlers.metaObjectHandler可以实现字段填充功能。但如果在更新实体,使用boolean update(Wrapper updateWrapper)这个方法进行更新时,则自动填充会失效。今天就来聊聊这个话题,本文例子使用的mybatis-plus版本为3.1.2版本

为何使用boolean update(Wrapper updateWrapper),自动填充会失效?

mybatis-plus 3.1.2版本跟踪源码,可以得知,自动填充的调用代码实现逻辑是由下面的核心代码块实现

 
    protected static Object populateKeys(metaObjectHandler metaObjectHandler, TableInfo tableInfo,
      MappedStatement ms, Object parameterObject, boolean isInsert) {
 if (null == tableInfo) {
     
     return parameterObject;
 }
 
 metaObject metaObject = ms.getConfiguration().newmetaObject(parameterObject);
 // 填充主键
 if (isInsert && !StringUtils.isEmpty(tableInfo.getKeyProperty())
     && null != tableInfo.getIdType() && tableInfo.getIdType().getKey() >= 3) {
     Object idValue = metaObject.getValue(tableInfo.getKeyProperty());
     
     if (StringUtils.checkValNull(idValue)) {
  if (tableInfo.getIdType() == IdType.ID_WORKER) {
      metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getId());
  } else if (tableInfo.getIdType() == IdType.ID_WORKER_STR) {
      metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getIdStr());
  } else if (tableInfo.getIdType() == IdType.UUID) {
      metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.get32UUID());
  }
     }
 }
 if (metaObjectHandler != null) {
     if (isInsert && metaObjectHandler.openInsertFill()) {
  // 插入填充
  metaObjectHandler.insertFill(metaObject);
     } else if (!isInsert) {
  // 更新填充
  metaObjectHandler.updateFill(metaObject);
     }
 }
 return metaObject.getOriginalObject();
    }

从源码分析我们可以得知当tableInfo为null时,是不走自动填充逻辑。而tableInfo又是什么从地方进行取值,继续跟踪源码,我们得知tableInfo可以由底下代码获取

 if (isFill) {
     Collection parameters = getParameters(parameterObject);
     if (null != parameters) {
  List objList = new ArrayList<>();
  for (Object parameter : parameters) {
      TableInfo tableInfo = TableInfoHelper.getTableInfo(parameter.getClass());
      if (null != tableInfo) {
   objList.add(populateKeys(metaObjectHandler, tableInfo, ms, parameter, isInsert));
      } else {
   
   objList.add(parameter);
      }
  }
  return objList;
     } else {
  TableInfo tableInfo = null;
  if (parameterObject instanceof Map) {
      Map map = (Map) parameterObject;
      if (map.containsKey(Constants.ENTITY)) {
   Object et = map.get(Constants.ENTITY);
   if (et != null) {
if (et instanceof Map) {
    Map realEtMap = (Map) et;
    if (realEtMap.containsKey(Constants.MP_OPTLOCK_ET_ORIGINAL)) {
 tableInfo = TableInfoHelper.getTableInfo(realEtMap.get(Constants.MP_OPTLOCK_ET_ORIGINAL).getClass());
    }
} else {
    tableInfo = TableInfoHelper.getTableInfo(et.getClass());
}
   }
      }
  } else {
      tableInfo = TableInfoHelper.getTableInfo(parameterObject.getClass());
  }

从源码可以很清楚看出,tableInfo 的获取依赖parameterObject.getClass(),则这个parameterObject就是数据库插入或者更新对象。即我们的实体对象,当实体对象为null时,则tableInfo 的值也是为null,这就会导致自动填充失效

我们再来看下boolean update(Wrapper updateWrapper)这个代码的底层实现

default boolean update(Wrapper updateWrapper) {
 return this.update((Object)null, updateWrapper);
    }

通过代码我们可以知道,当使用这个方法时,其实体对象是null,导致调用自动填充方法时,得到的tableInfo是null,因而无法进入自动填充实现逻辑,因此导致填充自动失效

如何解决update(Wrapper updateWrapper),自动填充不生效问题

通过源码分析我们得知,只要tableInfo不为空,则就会进入自动填充逻辑,而tableInfo不为空的前提是更新或者插入的实体不是null对象,因此我们的思路就是在调用update方法时,要确保实体不为null

方案一:实体更新时,直接使用update(Wrapper updateWrapper)的重载方法boolean update(T entity, Wrapper updateWrapper)

示例:

msgLogService.update(new MsgLog(),lambdaUpdateWrapper)
方案二:重写update(Wrapper updateWrapper)方法

重写update的方法思路有如下

方法一:重写ServiceImpl的update方法

其核心思路如下,重写一个业务基类baseServiceImpl

public class baseServiceImpl, T> extends ServiceImpl  {

    
    @Override
    public boolean update(Wrapper updateWrapper) {
 T entity = updateWrapper.getEntity();
 if (null == entity) {
     try {
  entity = this.currentModelClass().newInstance();
     } catch (InstantiationException e) {
 e.printStackTrace();
     } catch (IllegalAccessException e) {
  e.printStackTrace();
     }
 }
 return update(entity, updateWrapper);
    }
}

业务service去继承baseServiceImpl,形如下

@Service
public class MsgLogServiceImpl extends baseServiceImpl implements MsgLogService {

}

方法二:通过动态代理去重写update(Wrapper updateWrapper)

其核心代码如下

@Aspect
@Component
@Slf4j
public class UpdateWapperAspect implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    private  Map entityMap = new HashMap<>();

    @Pointcut("execution(* com.baomidou.mybatisplus.extension.service.IService.update(com.baomidou.mybatisplus.core.conditions.Wrapper))")
    public void pointcut(){

    }

    @Around(value = "pointcut()")
    public Object around(ProceedingJoinPoint pjp){
 Object updateEnityResult = this.updateEntity(pjp);
 if(ObjectUtils.isEmpty(updateEnityResult)){
     try {
  return pjp.proceed();
     } catch (Throwable throwable) {
  throwable.printStackTrace();
     }
 }
 return updateEnityResult;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
 this.applicationContext = applicationContext;
    }

    
    private Object updateEntity(ProceedingJoinPoint pjp){
 Object[] args = pjp.getArgs();
 if(args != null && args.length == 1){
     Object arg = args[0];
     if(arg instanceof Wrapper){
  Wrapper updateWrapper = (Wrapper)arg;
  Object entity = updateWrapper.getEntity();
  IService service = (IService) applicationContext.getBean(pjp.getTarget().getClass());
  if(ObjectUtils.isEmpty(entity)){
      entity = entityMap.get(pjp.getTarget().getClass().getName());
      if(ObjectUtils.isEmpty(entity)){
   Class entityClz = ReflectionKit.getSuperClassGenericType(pjp.getTarget().getClass(), 1);
   try {
entity = entityClz.newInstance();
   } catch (InstantiationException e) {
log.warn("Entity instantiating exception!");
   } catch (IllegalAccessException e) {
log.warn("Entity illegal access exception!");
   }
   entityMap.put(pjp.getTarget().getClass().getName(),entity);
      }

  }
  return service.update(entity,updateWrapper);
     }
 }

 return null;

    }
}
总结

文章开头一直在指明mybatis-plus版本,是因为我跟过mybatis-plus3.1版本、3.3版本、3.4版本的自动填充的调用源码,其源码的实现各有不同,因为我github上的mybatis-plus引用的版本是3.1.2版本,因此就以3.1.2版本进行分析。不过其他版本的分析思路大同小异,都是去跟踪什么地方调用了自动填充的逻辑。

至于解决方案的几种思路,说下我的个人建议,如果项目初期的话,做好宣导,建议使用方案一,直接使用update(new MsgLog(),lambdaUpdateWrapper)这种写法。如果项目开发到一定程度了,发现很多地方都存在更新自动填充失效,则推荐使用直接底层重写update的方案

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号