此篇文章与前面:多线程:Future批量插入数据到mysql(一) 对应的解决同一个问题得两种方法。
即:多线程,向数据库批量插入数据
步骤:第一步,在控制层构造一百万数据(实际开发可能是接收其他地方的数据)
package com.sinux.cc.batchoperate.controller;
import com.sinux.cc.batchoperate.entity.SysUser;
import com.sinux.cc.batchoperate.service.ISysUserService;
import com.sinux.cc.utils.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
/**
* @author
* @since 2020-06-26
*/
@RestController
@RequestMapping("/batchoperate/sys-user")
@Api(tags = "批量操作")
public class SysUserController {
@Autowired
private ISysUserService iSysUserService;
@PostMapping("/batchInsertbyrun")
@ApiOperation(value = "Run方法多线程:批量插入数据")
public R batchInsertByRun() {
List<SysUser> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
SysUser su = new SysUser();
su.setUsername("美美" + i);
su.setPassword("i love you " + i);
su.setRoleId(i);
su.setLoginTime(new Date());
list.add(su);
}
boolean a = iSysUserService.batchInsertByRun(list);
if (a) {
return R.ok();
} else {
return R.error();
}
}
}
接口:interface
boolean batchInsertByRun(List<SysUser> list);
②在实现类中调用 runnable(记住:runnable方法的使用)
@Override
public boolean batchInsertByRun(List<SysUser> list){
try {
new Thread(new BatchRunnable(list,sysUserMapper)).start();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
③写好BatchRunnable类处理批量数据(***重点***这个类没有纳入容器管理,所以这里的list和sysUserMapper都是通过构造传过来的,记住这种思维方式)
package com.sinux.cc.batchoperate.util;
import com.sinux.cc.batchoperate.entity.SysUser;
import com.sinux.cc.batchoperate.mapper.SysUserMapper;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class BatchRunnable implements Runnable{
/**
* 100条为分界批量导入
*/
private int batch500 = 500;
/**mysql数据*/
private List<SysUser> list;
private SysUserMapper sysUserMapper;
ExecutorService executorService = new ThreadPoolExecutor(
4,
Runtime.getRuntime().availableProcessors(),
2L,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(12),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
public BatchRunnable(List<SysUser> list,SysUserMapper sysUserMapper){
System.out.println("当前线程:"+Thread.currentThread().getName());
this.list=list;
this.sysUserMapper=sysUserMapper;
}
@Override
public void run() {
try {
batchOp(list);
} catch (Exception e) {
e.printStackTrace();
}
}
private void batchOp(List<SysUser> list) {
System.out.println("进入batchOp===集合长度为:"+list.size());
if(!list.isEmpty()){
Long t1 = System.currentTimeMillis();
Integer size = list.size();
if(size<=batch500){
sysUserMapper.insertBatch(list);
}else {
batchSpilitList(list);
}
Long t2 = System.currentTimeMillis();
System.out.println("插入完毕,用时:"+(t2-t1)+"毫秒");
}
}
private void batchSpilitList(List<SysUser> list) {
Long t1 = System.currentTimeMillis();
List<List<SysUser>> lists = new ArrayList<>();
int i=1;
try {
lists = BathUtil.pagingList(list,batch500);
for(List<SysUser> li : lists){
System.out.println("第"+i+"批数据正在插入");
i++;
executorService.execute(()->{
System.out.println("当前线程是:"+Thread.currentThread().getName());
batchOp(li);
});
}
} catch (Exception e) {
e.printStackTrace();
}finally {
executorService.shutdown();
Long t2 = System.currentTimeMillis();
System.out.println("执行完成,用时…………"+(t2-t1));
}
}
}
④ list集合的分割方法(记住:通用方法)
public class BathUtil {
public static <T> List<List<T>> pagingList(List<T> list, int pageSize) {
int length = list.size();
System.out.println(pageSize);
int num = (length + pageSize - 1)/ pageSize;
List<List<T>> newlist = new ArrayList<>();
for (int i = 0; i < num; i++) {
int fromIndex = i * pageSize;
int toIndex = (i + 1) * pageSize < length ? (i + 1) * pageSize : length;
newlist.add(list.subList(fromIndex, toIndex));
}
return newlist;
}
}
⑤剩下的就是mapper和xml
void insertBatch(@Param("list") List<SysUser> list);
<insert id="insertBatch">
insert into sys_user(
username,
password,
role_id,
login_time
)values
<foreach collection="list" item="item" separator=",">
(
#{item.username},
#{item.password},
#{item.roleId},
#{item.loginTime}
)
</foreach>
</insert>
这里的实体类很简单:
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class SysUser implements Serializable {
private static final long serialVersionUID = 1L;
@TableId
private int id;
private String username;
private String password;
private Integer roleId;
private Date loginTime;
}
总结:1.在service层使用runnable的方法。在Future批量插入数据到mysql(一)中讲的是使用future实行多线程,对比总结归纳。
2.BatchRunnable 类的run方法开始使用了线程池处理批量数据插入,小批量插入数据的思维值得注意
3.集合分割BathUtil ,纯粹的技术方法。记住就好,可能会再遇到,这里是个通用的方法
4.xml里面的批量插入数据方法 foreach的使用(用的时候记得去哪里找就行)
<insert id="insertBatch">
insert into sys_user( login_time )values
<foreach collection="list" item="item" separator=",">
( #{item.loginTime})
</foreach>
</insert>