场景描述:
现在公司是sqlserver到mysql,要用代码实现初始化的数据搬迁工作,暴露出来初始化接口
问题描述:
数据库使用的是mybatis框架,对于有的表,例如97个字段,一百多万条数据,直接就内存溢出了。
耗时日志(同时优化了sql语句筛选了数据,如果是百万的表筛选了一半):
本次操作共耗时:约11秒,约0分钟,共50000条数据
本次操作共耗时:约10秒,约0分钟,共50000条数据
本次操作共耗时:约55秒,约0分钟,共440000条数据
本次操作共耗时:约0秒,约0分钟,共115条数据
本次操作共耗时:约0秒,约0分钟,共1986条数据
本次操作共耗时:约0秒,约0分钟,共1986条数据
本次操作共耗时:约0秒,约0分钟,共1335条数据
本次操作共耗时:约0秒,约0分钟,共1335条数据
本次操作共耗时:约0秒,约0分钟,共22条数据
解决办法:
① mapper.xml优化
传入类型为list集合,使用foreach来遍历拼接语句
insert into oa_workflow_flownode (workflowid, nodeid, nodename,
drawxpos, drawypos, nodetype,
nodetypeCN, isstart, isreject,
isend, version, description
)
values
(
#{item.workflowid,jdbcType=VARCHAR}, #{item.nodeid,jdbcType=VARCHAR}, #{item.nodename,jdbcType=VARCHAR},
#{item.drawxpos,jdbcType=INTEGER}, #{item.drawypos,jdbcType=INTEGER}, #{item.nodetype,jdbcType=INTEGER},
#{item.nodetypecn,jdbcType=VARCHAR}, #{item.isstart,jdbcType=INTEGER}, #{item.isreject,jdbcType=INTEGER},
#{item.isend,jdbcType=INTEGER}, #{item.version,jdbcType=CHAR}, #{item.description,jdbcType=VARCHAR}
)
② 对需要插入的数据进行分页插入,分页大小自己控制,随手一测就出来,不同的分页使用不同的分页大小
OAController.java中部分代码
//查询OA系统中的workflow_nodelink表数据,导入到bpm系统中的oa_workflow_nodelink表中
public void importOAWorkflowNodelink() throws Exception {
try {
//查询OA系统中的workflow_nodelink表数据
List recordList = oaWorkflowNodelinkService.getList();
//加入UUID,这是id
for (OAWorkflowNodelink oaWFN : recordList) {
oaWFN.setId(UUID.randomUUID().toString());
oaWFN.setVersion("systeminit");
}
//清空表
oaWorkflowNodelinkService.deleteAll();
//导入到bpm系统中的oa_workflow_flownode表中
batchProcess(OAWorkflowNodelink.class, 10000, recordList);
} catch (Exception e) {
e.printStackTrace();
throw new Exception(this.getClass().getName() + "发生一些错误");
}
}
/**
* 对于大量数据的插入操作进行分批处理节约时间
* 效能就会提升很多很多倍
*
* @param type 实例class
* @param pageSize 分页大小
* @param list 实例的集合
*/
public void batchProcess(Class type, int pageSize, List list) {
long start_time = System.currentTimeMillis();//开始执行时间
//分批数据信息
int totalSize = list.size(); //总记录数
int totalPage = totalSize / pageSize; //共N页
if (totalSize % pageSize != 0) {
totalPage += 1;
if (totalSize < pageSize) {
pageSize = list.size();
}
}
for (int pageNum = 1; pageNum < totalPage + 1; pageNum++) {
int starNum = (pageNum - 1) * pageSize;
int endNum = pageNum * pageSize > totalSize ? (totalSize) : pageNum * pageSize;
if (type == OAWorkflowNodelink.class) {
oaWorkflowNodelinkService.insertForeach(list.subList(starNum, endNum));
}
}
logger.info("[" + type.getName() + "]本次操作共耗时:约" +
(System.currentTimeMillis() - start_time) / 1000 + "秒,约" +
(System.currentTimeMillis() - start_time) / 60000 + "分钟,共" +
totalPage * pageSize + "条数据");
}
代码遗憾:
真的很想写一个工具类,然后通过工具类来反射达到自己new对象调用方法,因为每个对象里面的调用的方法都是insertForeach,但是ssm通过工厂了,如果我在工具类中通过new controller层对象的实现方式,那么在service层的dao层的注解注入就没有办法注入成功,除非使用spring-mvc.xml来配置bean,如果这样的话,就太麻烦了,所以我在这里就放弃这样,这会使代码更加复杂难读,毕竟还是要留给后人的,我就不恶心他们了。还是直接写在当前使用的controller下好了。
贴一下,如何通过反射实现调用方法,如果那些方法中没有对象是通过注解方式实现的,就可以使用。
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.List;
/**
* 关于此系统的数据库操作工具类
*
* @author xiaheshun
*/
public class DBUtils {
private static Logger logger = LoggerFactory.getLogger(DBUtils.class);
/**
* 对于大量数据的插入操作进行分批处理节约时间
* pagesize越大,插入越快
* 自己随便通过数据库测一下那个字段能用多大是最佳的
*
* @param type 实例class
* @param pageSize 分页大小
* @param list 实例的集合
*/
public static void batchProcess(Class serviceType, int pageSize, List list) throws Exception {
long start_time = System.currentTimeMillis();//开始执行时间
//分批数据信息
int totalSize = list.size(); //总记录数
int totalPage = totalSize / pageSize; //共N页
if (totalSize % pageSize != 0) {
totalPage += 1;
if (totalSize < pageSize) {
pageSize = list.size();
}
}
for (int pageNum = 1; pageNum < totalPage + 1; pageNum++) {
int starNum = (pageNum - 1) * pageSize;
int endNum = pageNum * pageSize > totalSize ? (totalSize) : pageNum * pageSize;
Method insertForeach = serviceType.getMethod("insertForeach", List.class);
insertForeach.invoke(list.subList(starNum, endNum));
}
logger.info("本次操作共耗时:" +
(System.currentTimeMillis() - start_time) / 1000 + "秒,约" +
(System.currentTimeMillis() - start_time) / 60000 + "分钟");
}
}