如何解决mybatis

您所在的位置:网站首页 mybatisplus修改数据updatewrapper 如何解决mybatis

如何解决mybatis

2024-07-11 04:34| 来源: 网络整理| 查看: 265

前言

使用过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版本跟踪源码,可以得知,自动填充的调用代码实现逻辑是由下面的核心代码块实现

/** * 自定义元对象填充控制器 * * @param metaObjectHandler 元数据填充处理器 * @param tableInfo 数据库表反射信息 * @param ms MappedStatement * @param parameterObject 插入数据库对象 * @return Object */ 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()); /* 自定义 ID */ 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 extends ServiceImpl { /** * * * @param updateWrapper * @return */ @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; } /** *重写update(Wrapper updateWrapper), 更新时自动填充不生效问题 * @param pjp * @return */ 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的方案

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-mybatisplus-tenant



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3