文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

让mybatis-plus支持null字段全量更新

2023-10-07 11:47

关注

文章目录

背景

如果仅仅只是所列的目标,那么mybatis-plus 中可以通过设置
mybatis-plus.global-config.db-config.field-strategy=ignored
来忽略null判断,达到实体字段为null时也可以更新数据为null
但是一旦使用了这个策略,就相当于所有业务代码都会按照这个策略执行。
但是我们的业务往往需要如下支持
1、支持null字段全部更新
2、支持非null更新
3、支持指定null字段更新
所以单独设置某一个策略是很难满足实际的业务场景,因此我们需要在写具体业务代码的时候能够根据需要选择合适的方式。

mybatis-plus字段的四种策略

这四种策略既可以配置全局,也可以在实体的注解上配置,但是,配置之后就是死的玩意,无法动态。

一般默认情况下都是not_null,如果此时要更新为null,那么用lambdaUpdateWrapper手动设置哪些字段需要更新为null:
如将userName字段更新为null

userService.update(                Wrappers.lambdaUpdate(user)                        .set(User::getUserName, null)                        .eq(User::getId,"0001"));

很显然字段较少时这个方案还能说的过去,但是我们既有很少字段的情况,也有大批量字段的情况
所以此时使用这种方案很明显的使用起来非常难受,那么有没有方案既能支持有值更新,又能支持指定更新,还能
支持全量更新呢?
答案是有的,提供一个最低成本的适配方案如下

方案一

全局设置字段策略为not_null
因为本身LambdaUpdateWrapper 已经满足了单个设置的需求,所以我们在写个方法把全部字段组装起来即可,
当然此处的全部字段肯定也不是真的全部字段比如:一些比较特别的字段就不能被更新为null

public class WrappersFactory {    private final static List<String> ignoreList = new ArrayList<>();    static {        ignoreList.add(CommonField.available);        ignoreList.add(CommonField.create_time);        ignoreList.add(CommonField.create_username);        ignoreList.add(CommonField.update_time);        ignoreList.add(CommonField.update_username);        ignoreList.add(CommonField.create_user_code);        ignoreList.add(CommonField.update_user_code);        ignoreList.add(CommonField.deleted);    }    public static <T> LambdaUpdateWrapper<T> updateWithNullField(T entity) {        UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();        List<Field> allFields = TableInfoHelper.getAllFields(entity.getClass());        MetaObject metaObject = SystemMetaObject.forObject(entity);        for (Field field : allFields) {            if (!ignoreList.contains(field.getName())) {                Object value = metaObject.getValue(field.getName());                updateWrapper.set(StringUtils.camelToUnderline(field.getName()), value);            }        }        return updateWrapper.lambda();    }}

使用

userService.update(WrappersFactory.updateWithNullField(user).eq(User::getId,"0001"));

方案二

此方案采用的是常规的mybatis-plus扩展
实际就是实现 IsqlInjector

定义个方法

public class UpdateWithNull extends AbstractMethod {    @Override    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {        //具体的字段逻辑在这里处理,实际上就是在这里构造一个新的statement        return null;    }}

定义个CommonMapper继承自BaseMapper,然后让你的所有Mapper继承自CommonMapper

public interface CommonMapper <T> extends BaseMapper<T> {        int updateWithNull(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);}

声明一个IsqlInjector,然后将其配置为spring的bean即可

public class CustomSqlInjector extends AbstractSqlInjector {    @Override    public List<AbstractMethod> getMethodList() {        return Stream.of(                new Insert(),                new Delete(),                new DeleteByMap(),                new DeleteById(),                new DeleteBatchByIds(),                new Update(),                new UpdateWithNull(),                new UpdateById(),                new SelectById(),                new SelectBatchByIds(),                new SelectByMap(),                new SelectOne(),                new SelectCount(),                new SelectMaps(),                new SelectMapsPage(),                new SelectObjs(),                new SelectList(),                new SelectPage()        ).collect(toList());    }}

方案二个人认为没有什么必要,这种扩展方式是为了增加一些mybatis-plus未支持的定式需求。而我们的目标相对简单,所以使用方案一更高效。

方案二原理介绍

TableFieldInfo#getSqlSet

public String getSqlSet(final String prefix) {        final String newPrefix = prefix == null ? EMPTY : prefix;        // 默认: column=        String sqlSet = column + EQUALS;        if (StringUtils.isNotEmpty(update)) {            sqlSet += String.format(update, column);        } else {            sqlSet += SqlScriptUtils.safeParam(newPrefix + el);        }        sqlSet += COMMA;        if (fieldFill == FieldFill.UPDATE || fieldFill == FieldFill.INSERT_UPDATE) {            // 不进行 if 包裹            return sqlSet;        }        return convertIf(sqlSet, newPrefix + property);    }

可以看到这段代码的逻辑中有一行fieldFill判断,为update或者insert_update时不进行if包裹。我们能可以利用这个特性。直接将需要的非公共字段全部设置为FieldFill.UPDATE即可。

final List<TableFieldInfo> fieldList = tableInfo.getFieldList();        for (final TableFieldInfo tableFieldInfo : fieldList) {            final Class<? extends TableFieldInfo> aClass = tableFieldInfo.getClass();            try {                final Field fieldFill = aClass.getDeclaredField("fieldFill");                fieldFill.setAccessible(true);                fieldFill.set(tableFieldInfo, FieldFill.UPDATE);            } catch (final NoSuchFieldException e) {                log.error("获取fieldFill失败", e);            } catch (final IllegalAccessException e) {                log.error("设置fieldFill失败", e);            }        }

所以这里的具体逻辑为

@Slf4jpublic class UpdateWithNull extends AbstractMethod {    @Override    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {        SqlMethod sqlMethod = SqlMethod.UPDATE;        final List<TableFieldInfo> fieldList = tableInfo.getFieldList();        for (final TableFieldInfo tableFieldInfo : fieldList) {            final Class<? extends TableFieldInfo> aClass = tableFieldInfo.getClass();            try {                final Field fieldFill = aClass.getDeclaredField("fieldFill");                fieldFill.setAccessible(true);                fieldFill.set(tableFieldInfo, FieldFill.UPDATE);            } catch (final NoSuchFieldException e) {                log.error("获取fieldFill失败", e);            } catch (final IllegalAccessException e) {                log.error("设置fieldFill失败", e);            }        }        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(),                sqlSet(true, true, tableInfo, true, ENTITY, ENTITY_DOT),                sqlWhereEntityWrapper(true, tableInfo));        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);        return addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);    }}

来源地址:https://blog.csdn.net/a807719447/article/details/129008176

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯