返回顶部
首页 > 资讯 > 数据库 >让mybatis-plus支持null字段全量更新
  • 596
分享到

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

mybatisjavamysql 2023-10-07 11:10:25 596人浏览 独家记忆
摘要

文章目录 背景方案一使用方案二方案二原理介绍 背景 如果仅仅只是标题所列的目标,那么mybatis-plus 中可以通过设置 mybatis-plus.global-config

文章目录

背景

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

mybatis-plus字段的四种策略

  • default 默认的,一般只用于注解里
    • 在全局里代表 NOT_NULL
    • 在注解里代表 跟随全局
  • ignored 忽略判断
  • not_empty 非空判断
  • not_null 非NULL判断

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

一般默认情况下都是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

您可能感兴趣的文档:

--结束END--

本文标题: 让mybatis-plus支持null字段全量更新

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

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

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作