Update批量更新(⾼性能、动态化)
⽂章⽬录
前⾔
MySQL数据库批量更新功能,如果数量量⽐较⼤,同时更新的内容是依据DO类不同的字段值对应更新,此情景下市⾯上的框架
MyBatis(Plus) 执⾏的批量更新仅仅是依据主键id循环遍历单条更新,极⼤的影响了性能。
在此背景下,结合MySQL的语法和MyBatis-Plus框架的预编译功能,通过代码实现⾼性能、动态化的批量更新,并可配置分段执⾏⼤⼩。注意:⼀定看完本⽂,并且深知使⽤条件和存在的弊端,否则不建议轻易采⽤!!
⼀、环境
开发环境
JDK8
maven  3.6.3
SpringBoot  2.3.9.RELEASE
MySQL  5.7
MyBatisPlus  3.4.2
lombok  1.18.18
hutool  5.6.0
必备条件:JDK、MySQL、MyBatis-Plus、SpringIOC
测试环境
环境简述
Win10版本2004
内存16G
硬盘512G SSD
编辑器IDEA2020.3
⼆、灵光乍现
最近项⽬中连续⽤到批量更新的功能,通过查看MyBatis-Plus源码以及打印执⾏SQl发现,MyBatis-Plus封装的批量更新⽅法不尽⼈意。
当然这样的逻辑对不同场景都有⾮常好的灵活性,当然我们知道,在程序中灵活与性能⼀直是相爱相杀的冤家对头,程序很难在保证⾼灵活性的同时兼顾⾼性能,毕竟鱼和熊掌不可兼得。
MyBatis-Plus源码
// MyBatis-Plus封装的IService接⼝的UpdateBatch⽅法
@Transactional(
rollbackFor ={Exception.class}
)
default boolean updateBatchById(Collection<T> entityList){
return this.updateBatchById(entityList,1000);
}
boolean updateBatchById(Collection<T> entityList,int batchSize)
// ServiceImpl对应的UpdateBatch实现
public boolean updateBatchById(Collection<T> entityList,int batchSize){
String sqlStatement =SqlStatement(SqlMethod.UPDATE_BY_ID);
uteBatch(entityList, batchSize,(sqlSession, entity)->{
ParamMap<T> param =new ParamMap();
param.put("et", entity);
sqlSession.update(sqlStatement, param);
});
}
// 遍历执⾏
public static<E>boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list,int batchSize, BiConsumer<SqlSession, E> consumer){
Assert.isFalse(batchSize <1,"batchSize must not be less than one",new Object[0]);
return!CollectionUtils.isEmpty(list)&&executeBatch(entityClass, log,(sqlSession)->{
int size = list.size();
int i =1;
// 便利传⼊的list,拼接执⾏SQL,并循环执⾏
for(Iterator var6 = list.iterator(); var6.hasNext();++i){
E element = ();
consumer.accept(sqlSession, element);
if(i % batchSize ==0|| i == size){
sqlSession.flushStatements();
}
}
});
}
2.初见真正的批量更新语法
受博主分析并描述的SQL影响,促使我萌⽣⽤Java并配合MyBaitsPlus预编译实现动态批量UpdateBatchById的代码。
在此,向御世制⼈表⽰感谢,同时⽹上也有很多与之类似的描述,在这⾥就不⼀⼀描述了。只把核⼼的代码拼出来。
UPDATE sys_user
SET phone=CASE id
WHEN2THEN'153********'
WHEN4THEN'153********'
WHEN6THEN'153********'END,
email=CASE id
WHEN2THEN'1111@qq'
WHEN4THEN'2222@qq'
WHEN6THEN'3333@qq'END
WHERE id IN('2','4','6');
核⼼逻辑:利⽤CASE-WHEN语法,同时主键作为Where条件,使多语句拼成⼀条SQL成为了可能。
如果少量的更新,可以通过MyBatis的xml实现,但需要对每⼀个DO类做定制化SQL。故此,便萌⽣了Java代码动态拼接UpdateBatch SQL的想法。
此语法带来可能的同时,也隐藏了⼀个,详情看 ⽬录五。
三、开⼯
基础类搭建
SysUser(表sys_user实体类)
假设我们是对数据库表sys_user的更新,对应的DO类 SysUser package am.entity;
import batisplus.annotation.TableName;
import lombok.Data;
perimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 系统⽤户表(SysUser)表实体类
*
* @author utrix
* @since 2021-02-17 10:56:55
*/
@Data
@Accessors(chain =true)
@TableName("sys_user")
public class SysUser implements Serializable {
private static final long serialVersionUID =-43087234816700913L;
/
**
* 主键
*/
private Long id;
/**
* ⽤户名
*/
private String username;
/**
* 邮箱
*/
private String email;
/**
* ⼿机号
*/
private String phone;
}
Stash(拼接SQL服务,内部类)
/**
* 为了拼接更新sql了创建的存储内部类,在SQL拼接类MakeSqlUtil中
*/
@Data
private static class Stash {
private Object id;
private Object value;
}
TableCacheDTO(数据表信息存储)
package am.domain.dto;
import lombok.Data;
perimental.Accessors;
import java.io.Serializable;
import java.util.Map;
/**
* 数据库表基本信息缓存类
*
* @author utrix
* @date 2021/6/22
*/
@Data
@Accessors(chain =true)
public class TableCacheDTO implements Serializable {
private static final long serialVersionUID =4904690745405851925L;
/**
* 表名
*/
private String tableName;
/**
* 主键名
*/
private String keyColumn;
/**
* 表字段集合
* <pre>
*    key: 实体类fieldName
*    value: 对应的表列名
批量更新sql语句* </pre>
*/
private Map<String, String> fieldColumn;
}
TableCache(表信息缓存)
package am.expand;
llect.Maps;
import lombok.Getter;
import am.domain.dto.TableCacheDTO;
import java.util.Map;
/**
* 表信息缓存类
*
* @author utrix
* @date 2021/6/22
*/
public class TableCache {
/**
* 数据库表信息集合
*/
@Getter
private static final Map<String, TableCacheDTO> TABLE_MAPS = wHashMap(); }
MySQL拼接常量类
package am.constant;
/**
* SQL拼接执⾏常量类
*
* @author utrix
* @date 2021/6/21
*/
public interface SqlConstant {
// ============= 符号 =================
/**
* 符号之空格
*/
String SPACE =" ";
/**
* 半⾓逗号
*/
String COMMA =",";
/**
* 单引号
*/
String APOSTROPHE ="'";
/
**
* 等于
*/
String EQUAL ="="+ SPACE;
/**
* 符号之左括号
*/
String LEFT_PARENTHESIS = SPACE +"(";
/**
* 符号之右括号
*/
String RIGHT_PARENTHESIS =")";
// ================= SQL语法函数 ==================
/**
* 数据操纵语⾔(Data Manipulation Language) 之更新
*/
String DML_UPDATE ="UPDATE"+ SPACE;
/**
* 语法之 SET
*/
String SQL_SET = SPACE +"SET"+ SPACE;
/**
* SQL函数之 CASE
*/
String FUNC_CASE ="CASE"+ SPACE;
/**
* 等号 + CASE
*/
String EQUAL_CASE = EQUAL + FUNC_CASE;
/**
* SQL CASE-WHEN函数之 WHEN
*/
String FUNC_WHEN = SPACE +"WHEN"+ SPACE;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。