mybatis-plus批量更新updateBatchById问题

 更新时间:2023年07月14日 10:38:52   作者:KevinPan_1992  
这篇文章主要介绍了mybatis-plus批量更新updateBatchById问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

前言

在使用mybatis-plus过程中,有很多插件都特别优秀,不仅使我们代码更加优雅,也提升了效率。

其中有个批量插入的插件insertBatchSomeColumn使用起来也挺方便的,但是批量更新一直没有官方插件,网络上面也没有找到靠谱的,于是就参照mybatis-plus这些官方的方法自定义了一个批量更新的方法。

实现效果

案例:用户排序

最终更新语句:

UPDATE sys_user 
SET user_order =
CASE
		id 
		WHEN 1 THEN	1 
		WHEN 2 THEN	2 
		WHEN 3 THEN	3 
		WHEN 4 THEN	4 
END 
WHERE tenant_id = 1 
AND id IN (1,2,3,4)

批量新增插件的配置

定义一个自己的BaseMapper继承自mybatis-plus的BaseMapper,声明批量新增方法,如下:

public interface MyBaseMapper<T> extends BaseMapper<T> {
    /**
     * 批量插入
     *
     * @param entityList 实体列表
     * @return 影响行数
     */
    int insertBatchSomeColumn(Collection<T> entityList);
}

把批量新增方法添加到方法列表中:

/**
 * 通用方法注入
 */
public class MySqlInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        // 添加批量新增方法
        methodList.add(new InsertBatchSomeColumn());
        return methodList;
    }
}

MybatisPlusConfig配置中注入bean:

    @Bean
    public MySqlInjector mySqlInjector() {
        return new MySqlInjector();
    }

业务Mapper继承自自定义的MyBaseMapper,则就可以使用批量新增方法了。

下面进入正题

updateBatchById实现

自定义方法枚举

参照官方的SqlMethod,创建枚举MySqlMethod,并定义批量更新方法,如下:

public enum MySqlMethod {
    UPDATE_BATCH_BY_ID("updateBatchById", "通过主键批量更新数据", "<script>UPDATE %s \n%s \nWHERE %s IN %s\n</script>");
    private final String method;
    private final String desc;
    private final String sql;
    MySqlMethod(String method, String desc, String sql) {
        this.method = method;
        this.desc = desc;
        this.sql = sql;
    }
    public String getMethod() {
        return this.method;
    }
    public String getDesc() {
        return this.desc;
    }
    public String getSql() {
        return this.sql;
    }
}

自定义批量更新方法

定义UpdateBatchById继承自AbstractMethod,实现其抽象方法injectMappedStatement,功能就是拼接sql,具体实现如下:

/**
 * 通过ID批量更新
 */
public class UpdateBatchById extends AbstractMethod {
    private static final long serialVersionUID = 4198102405483580486L;
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        MySqlMethod sqlMethod = MySqlMethod.UPDATE_BATCH_BY_ID;
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), this.sqlSet(tableInfo), tableInfo.getKeyColumn(), this.sqlIn(tableInfo.getKeyProperty()));
        SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);
        return this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
    }
    private String sqlSet(TableInfo tableInfo) {
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        StringBuilder sb = new StringBuilder();
        for (TableFieldInfo fieldInfo : fieldList) {
            sb.append("<if test=\"ew.updateFields.contains(&quot;").append(fieldInfo.getColumn()).append("&quot;)\">")
                    .append(fieldInfo.getColumn()).append(" =\n")
                    .append("CASE ").append(tableInfo.getKeyColumn()).append("\n")
                    .append("<foreach collection=\"list\" item=\"et\" >\n")
                    .append("WHEN #{et.").append(tableInfo.getKeyProperty()).append("} THEN #{et.").append(fieldInfo.getProperty()).append("}\n")
                    .append("</foreach>\n").append("END ,\n")
                    .append("</if>\n");
        }
        return "<set>\n" + sb + "</set>";
    }
    private String sqlIn(String keyProperty) {
        StringBuilder sb = new StringBuilder();
        sb.append("<foreach collection=\"list\" item=\"et\" separator=\",\" open=\"(\" close=\")\">\n")
                .append("#{et.").append(keyProperty).append("}")
                .append("</foreach>\n");
        return sb.toString();
    }
}

到了这一步已经能够基本实现功能了,但是无法控制需要更新的字段,继续看下面。

自定义更新wrapper

自定义UpdateBatchWrapper继承自AbstractLambdaWrapper,此类主要为updateFields属性设置值,拼接sql的时候只对设置的属性更新,其他属性不变。

public class UpdateBatchWrapper<T> extends AbstractLambdaWrapper<T, UpdateBatchWrapper<T>> {
    private static final long serialVersionUID = 114684162001472707L;
    /**
     * 需要更新的字段
     */
    private List<String> updateFields = null;
    @Override
    protected UpdateBatchWrapper<T> instance() {
        this.updateFields = new ArrayList<>();
        return this;
    }
	/**
     * 关键代码,为属性设置值
     */
    @SafeVarargs
    public final UpdateBatchWrapper<T> setUpdateFields(SFunction<T, ?>... columns) {
        this.updateFields = Arrays.asList(columnsToString(columns).split(","));
        return this;
    }
    public List<String> getUpdateFields() {
        return updateFields;
    }
}

参照批量新增把方法添加到方法列表

MyBaseMapper增加配置:

 /**
     * 通过ID批量更新数据
     *
     * @param entityList 实体列表
     * @return 影响行数
     */
    int updateBatchById(@Param("list") Collection<T> entityList, @Param("ew") Wrapper<T> updateWrapper);

MySqlInjector增加配置:

// 添加批量更新方法
methodList.add(new UpdateBatchById());

测试updateBatchById

创建一个接口saveUserOrder实现用户排序,进而检查批量更新方法。

controller层

    @PostMapping("saveUserOrder")
    @ApiOperation("用户排序")
    public Result saveUserOrder(@RequestBody List<OrderUserSO> soList) {
        sysService.saveUserOrder(soList);
        return Result.success();
    }

service层

@Override
    public void saveUserOrder(List<OrderUserSO> soList) {
        // 业务实体转换为数据库实体
        List<SysUser> userList = JSONUtil.toList(JSONUtil.toJsonStr(soList), SysUser.class);
        // 批量更新-设置更新字段为userOrder
        sysUserMapper.updateBatchById(userList,
                new UpdateBatchWrapper<SysUser>()
                        .setUpdateFields(SysUser::getUserOrder));
    }

OrderUserSO实体

@Data
@ApiModel("用户排序业务实体")
public class OrderUserSO implements Serializable {
    private static final long serialVersionUID = 509541044282315352L;
    @ApiModelProperty(value = "用户ID", required = true)
    @NotNull
    private Integer id;
    @ApiModelProperty(value = "用户顺序", required = true)
    @NotNull
    private Integer userOrder;
}

见证奇迹的时刻到了…去文章开头见证奇迹吧。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot自定义定时任务的实现示例

    SpringBoot自定义定时任务的实现示例

    本文主要介绍了SpringBoot自定义定时任务,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-05-05
  • Mybatis使用@one和@Many实现一对一及一对多关联查询

    Mybatis使用@one和@Many实现一对一及一对多关联查询

    本文主要介绍了Mybatis使用@one和@Many实现一对一及一对多关联查询,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 基于idea Maven中的redis配置使用详解

    基于idea Maven中的redis配置使用详解

    这篇文章主要介绍了基于idea Maven中的redis配置使用,包括一些配置文件需要的内容,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2021-07-07
  • 一文带你深入理解Java AbstractQueuedSynchronizer

    一文带你深入理解Java AbstractQueuedSynchronizer

    在并发编程中,锁是一种保证线程安全的方式,这篇文章主要为大家介绍了AbstractQueuedSynchronizer(AQS)的数据结构及实现原理,感兴趣的小伙伴可以了解一下
    2023-07-07
  • springboot整合quartz项目使用案例

    springboot整合quartz项目使用案例

    quartz是一个定时调度的框架,就目前市场上来说,其实有比quartz更优秀的一些定时调度框架,不但性能比quartz好,学习成本更低,而且还提供可视化操作定时任务,这篇文章主要介绍了springboot整合quartz项目使用(含完整代码),需要的朋友可以参考下
    2023-05-05
  • StringUtils中的isEmpty、isNotEmpty、isBlank和isNotBlank的区别详解

    StringUtils中的isEmpty、isNotEmpty、isBlank和isNotBlank的区别详解

    这篇文章主要介绍了StringUtils中的isEmpty、isNotEmpty、isBlank和isNotBlank的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • springboot springmvc抛出全局异常的解决方法

    springboot springmvc抛出全局异常的解决方法

    这篇文章主要为大家详细介绍了springboot springmvc抛出全局异常的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • Springboot中如何通过yml为实体类注入属性

    Springboot中如何通过yml为实体类注入属性

    这篇文章主要介绍了Springboot中如何通过yml为实体类注入属性,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • 使用AbstractRoutingDataSource实现数据源动态切换的实例

    使用AbstractRoutingDataSource实现数据源动态切换的实例

    AbstractRoutingDataSource 是 Spring 框架提供的一个抽象类,用于实现动态数据源路由,这个类主要用于多数据源场景,其中可以根据不同的条件动态地切换到不同的数据源,本文给大家介绍了如何使用AbstractRoutingDataSource实现数据源动态切换,需要的朋友可以参考下
    2024-03-03
  • java实现推箱子小游戏

    java实现推箱子小游戏

    这篇文章主要为大家详细介绍了java实现推箱子小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-05-05

最新评论