在日常工作中,我们经常会遇到各种各样的需求,需要将数据插入到数据库中,不过不同的需求用不同的插入写法,这样能提升很大的效率,这篇文章会根据不同的场景使用不同的批量插入代码,大家以后遇到批量插入直接来我这里粘贴就可以了。
场景一:
app中最常见的pv和uv接口,用户每点击一次都需要在数据库中插入一条数据。这种情况我们不可能一直请求数据库,肯定会将数据先存到缓存或者队列中,然后批量插入到数据库。
上队列代码:
public void run() {
startLog();
while (!isInterrupted()) {
List<T> list = new ArrayList<T>();
preProcessor();
try {
long ts = 0;
//每一百条批量插入一次
for (int i = 0; i < 100; ) {
T tmp = null;
try {
tmp = queue.poll(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tmp != null) {
//记录第一次添加数据后的时间
if (i++ == 0) {
ts = System.currentTimeMillis();
}
//处理
processor(list, tmp);
continue;
}
//有数据 且从第一次插入数据已经过了 设定时间 则 执行插入
if (list.size() > 0 && System.currentTimeMillis() - ts >= BATCH_TIMEOUT) {
break;
}
}
save(list);
} catch (Exception e) {
e.printStackTrace();
}
}
}
上mybatis代码:
<insert id="batchInsert" parameterType="List">
insert into dsc_user
(
user_name, nick_name, mobile
)
values
<foreach collection='list' item='item' index='index' separator=','>
(
#{item.userName,jdbcType=VARCHAR},#{item.nickName,jdbcType=VARCHAR},#{item.mobile,jdbcType=VARCHAR}
)
</foreach>
</insert>
注:这个地方不能把foreach标签写到外层,否则还是会不停的创建关闭sqlsession
场景二:
某个功能导入数据总是千万级别,如何最快的将数据插入到数据库中
这种大数据量导入数据 和 小数据量导入是有很大区别的,同样的代码,小数据量导入可以完美实现,但数据量大了之后就会出现各种各样的问题
首先,我们要重视的是内存,如果要实现千万级别的数据导入,JVM的堆内存一定要设置一个比较大的数值
其次,要分段保存到数据库,比如每1000条数据执行一次
最后,不要使用mybatis,因为myabtis封装程度太高,解析SQL模板和SQL拼接会消耗大量时间,要使用原生的JDBC
上代码
public void insertBatch() {
Connection conn = null;
Statement statement = null;
try {
conn = DriverManager.getConnection(url, username, password);
conn.setAutoCommit(false);
statement = conn.createStatement();
for (int i = 0; i < 10000000; i++) {
String sql = "insert into dsc_user(user_name, nick_name, mobile)
values ('"+i+"', '第"+i+"条数据', '158'+"+ i +")";
//利用addBatch方法将SQL语句加入到stmt对象中
statement.addBatch(sql);
if (i % 1000 == 0 && i != 0) {
//利用executeBatch方法执行1000条SQL语句
statement.executeBatch();
statement.clearBatch();
conn.commit();
}
}
statement.executeBatch();
statement.clearBatch();
conn.commit();
close(); //关闭资源
} catch (SQLException e) {
e.printStackTrace();
}
}
一般情况下,我们很少遇到需要批量插入千万级别数据的,所以批量插入只需要在mybatis中使用一下foreach标签就能解决,如下代码
public void insertBatch(List<CouponHistory> coupons){
if (coupons == null || coupons.isEmpty()){
return;
}
int index = 1000;
int currentPageStart = 0;
int currentPageEnd = 0;
while (currentPageEnd < coupons.size()){
currentPageStart = currentPageEnd;
currentPageEnd = Math.min(currentPageEnd + index, coupons.size());
//调用mybatis批量插入
batchInsert(coupons.subList(currentPageStart, currentPageEnd));
}
}
<insert id="batchInsert" parameterType="List">
insert into dsc_user
(
user_name, nick_name, mobile
)
values
<foreach collection='list' item='item' index='index' separator=','>
(
#{item.userName,jdbcType=VARCHAR},#{item.nickName,jdbcType=VARCHAR},#{item.mobile,jdbcType=VARCHAR}
)
</foreach>
</insert>
大家在做批量插入时只需要将最后这块代码复制过去就可以啦
注:mybatis-plus中批量插入方法就是 for循环单条插入;
以上就是批量导入两个场景中的解决方案,今天就写到这里,再见啦!
昔我往矣,杨柳依依
今我来思,雨雪霏霏
【白话译文】
回想当初出征时,杨柳依依随风吹;如今回来路途中,大雪纷纷满天飞