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("").append(fieldInfo.getColumn()).append("")\">") .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; }
见证奇迹的时刻到了…去文章开头见证奇迹吧。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
Mybatis使用@one和@Many实现一对一及一对多关联查询
本文主要介绍了Mybatis使用@one和@Many实现一对一及一对多关联查询,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2021-09-09一文带你深入理解Java AbstractQueuedSynchronizer
在并发编程中,锁是一种保证线程安全的方式,这篇文章主要为大家介绍了AbstractQueuedSynchronizer(AQS)的数据结构及实现原理,感兴趣的小伙伴可以了解一下2023-07-07StringUtils中的isEmpty、isNotEmpty、isBlank和isNotBlank的区别详解
这篇文章主要介绍了StringUtils中的isEmpty、isNotEmpty、isBlank和isNotBlank的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2020-06-06springboot springmvc抛出全局异常的解决方法
这篇文章主要为大家详细介绍了springboot springmvc抛出全局异常的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2017-06-06使用AbstractRoutingDataSource实现数据源动态切换的实例
AbstractRoutingDataSource 是 Spring 框架提供的一个抽象类,用于实现动态数据源路由,这个类主要用于多数据源场景,其中可以根据不同的条件动态地切换到不同的数据源,本文给大家介绍了如何使用AbstractRoutingDataSource实现数据源动态切换,需要的朋友可以参考下2024-03-03
最新评论