备注:
两种方法:批量修改,新增 application.yml //允许多条数据,否则mybatis 插入数据报错
&allowMultiQueries=true
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://ip地址:3306/数据库名?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: 账号
password: 密码
(1)方法一
通过id等唯一性字段比较,获取updateList 和 insertList
List<String> list = operationLogService.getAllIdList();
List<OperationLog> updList = new ArrayList<>();
List<OperationLog> insertList = new ArrayList<>();
for (int i =0 ; i < operationLogs.size(); i++) {
OperationLog temp = operationLogs.get(i);
if (list.contains(String.valueOf(temp.getId())) ){
updList.add(temp);
} else {
insertList.add(temp);
}
}
boolean b = false;
if(insertList.size() > 0){
List<List<OperationLog>> listIns = ListUtil.averageAssign(insertList, 1000);
for(List<OperationLog> list1: listIns) {
b = operationLogService.batchInsert(list1);
}
}
if(updList.size() > 0 ){
List<List<OperationLog>> listUpd = ListUtil.averageAssign(updList, 500);
for(List<OperationLog> list2: listUpd) {
b = operationLogService.batchUpdate(list2);
}
}
List 集合拆分
public static <T> List<List<T>> averageAssign(List<T> source, int splitItemNum) {
List<List<T>> result = new ArrayList<List<T>>();
if (source != null && source.size() > 0 && splitItemNum > 0) {
if (source.size() <= splitItemNum) {
// 源List元素数量小于等于目标分组数量
result.add(source);
} else {
// 计算拆分后list数量
int splitNum = (source.size() % splitItemNum == 0) ? (source.size() / splitItemNum) : (source.size() / splitItemNum + 1);
List<T> value = null;
for (int i = 0; i < splitNum; i++) {
if (i < splitNum - 1) {
value = source.subList(i * splitItemNum, (i + 1) * splitItemNum);
} else {
// 最后一组
value = source.subList(i * splitItemNum, source.size());
}
result.add(value);
}
}
}
return result;
}
第二种办法
经过测试,批量更新比较慢,可以优化为 先删后全部插入的方案,提高相应时间
List<String> list = operationLogService.getAllIdList();
List<Integer> updList = new ArrayList<>();
for (int i =0 ; i < operationLogs.size(); i++) {
OperationLog temp = operationLogs.get(i);
if (list.contains(String.valueOf(temp.getId())) ){
updList.add(temp.getId());
}
}
boolean b = false;
if(updList.size() > 0 ){
operationLogService.removeByIds(updList);
}
if(operationLogs.size() > 0){
List<List<OperationLog>> listIns = ListUtil.averageAssign(operationLogs, 1000);
for(List<OperationLog> list1: listIns) {
b = operationLogService.batchInsert(list1);
}
}
第三种方案
采用线程池批量执行,进一步提高执行速度
public boolean threadMethod(List<OperationLog> updateList) {
boolean res = false;
// 初始化线程池,、
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 60,
4, TimeUnit.SECONDS, new ArrayBlockingQueue(10), new ThreadPoolExecutor.CallerRunsPolicy());
// 大集合拆分成N个小集合、
try{
List<List<OperationLog>> splitNList = ListUtil.averageAssign(updateList, 100);
// 记录单个任务的执行次数
CountDownLatch countDownLatch = new CountDownLatch(splitNList.size());
// 对拆分的集合进行批量处理, 先拆分的集合, 再多线程执行
for (List<OperationLog> singleList : splitNList) {
// 线程池执行
threadPool.execute(new Thread(new Runnable(){
@Override
public void run() {
if (singleList.size() > 0) {
operationLogService.batchUpdate(singleList);
// 任务个数 - 1, 直至为0时唤醒await()
countDownLatch.countDown();
}
}
}));
}
countDownLatch.await();
res = true;
}catch (Exception e){
res = false;
e.printStackTrace();
}finally {
threadPool.shutdown();
}
return res;
}
附录,其他模块
mapper
boolean batchInsert(List<OperationLog> list);
boolean batchUpdate(List<OperationLog> list);
xml
<select id="getAllIdList" resultType="String" >
select id from xxx order by id
</select>
<insert id="batchInsert" parameterType="com.highguard.sct.auditModule.entity.OperationLog">
insert into xxx (id, user_name, ...)
values
<foreach collection="list" item="item" separator=",">
(
#{item.id}, #{item.userName}, ...
)
</foreach>
</insert>
<update id="batchUpdate" parameterType="com.highguard.sct.auditModule.entity.OperationLog">
<foreach collection="list" item="item" index="index" separator=";">
update xxx set
<if test="item.userName != null and item.userName !=''">
user_name = #{item.userName},
</if>
<if test="item.userRole != null and item.userRole !=''">
user_role = #{item.userRole},
</if>
...
createtime = #{item.createtime}
<where>
id = #{item.id}
</where>
</foreach>
</update>
foreach foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。 foreach元素的属性主要有
item,index,collection,open,separator,close。item集合中每一个元素进行迭代时的别名, index表示在迭代过程中,每次迭代到的位置, open该语句以什么开始,
insert into shop_coupon_record( id, coupon_id, pick_date_time, use_date_time, member_id, is_use, create_date_time, delete_status ) select #{item.id}, #{item.couponId}, #{item.pickDateTime}, #{item.useDateTime}, #{item.memberId}, #{item.isUse}, #{item.createDateTime}, #{item.deleteStatus} from dual
separator在每次进行迭代之间以什么符号作为分隔 符, close以什么结束,
在使用foreach的时候最关键的也是最容易出错的就是collection属性, 该属性是必须指定的,但是在不同情况
下,该属性的值是不一样的, 主要有一下3种情况:
1.如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
2.如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
3.如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了