mybatis-plus批量更新updateBatchById问题
作者:KevinPan_1992
前言
在使用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; }
见证奇迹的时刻到了…去文章开头见证奇迹吧。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。