protected ThreadLocal localSqlMapSession = new ThreadLocal();
protected SqlMapSessionImpl getLocalSqlMapSession() {
SqlMapSessionImpl sqlMapSession =
(SqlMapSessionImpl)this.localSqlMapSession.get();
if (sqlMapSession == null || sqlMapSession.isClosed()) {
sqlMapSession = new SqlMapSessionImpl(this);
this.localSqlMapSession.set(sqlMapSession);
}
return sqlMapSession;
}
public int batchUpdate(String sqlId, List list) {
try {
if (list == null) {
throw new NullPointerException();
} else {
this.getLocalSqlMapSession().startBatch();
for(int i = 0; i < list.size(); ++i) {
this.getLocalSqlMapSession().update(sqlId, list.get(i));
}
return this.getLocalSqlMapSession().executeBatch();
}
} catch (SQLException var4) {
var4.printStackTrace();
throw new RuntimeException(var4.getMessage(), var4);
}
}
我们关注
if (statementScope.getSession().isInBatch()) {????????
若想要优化批量更新操作,我们需要控制每次批量操作的数量,例如我遇到的生产环境需要导入1.5w条数据,直接for循环后,全部sql放在List集合再去执行,就会很慢,此时我们需要重写batchUpdate方法,控制每次批量执行的sql语句的量(这里我控制每次批量执行3000条sql语句)
public int batchUpdate(List<ReceivableDataDetailEntity> list) {
this.logger.info("=============进入测试的batchUpdate方法===========");
try {
if (list == null) {
throw new NullPointerException();
} else {
this.sqlMapClient.startBatch();
int batch = 0;
for(int i = 0; i < list.size(); ++i) {
logger.info("=============第" +batch+ "次===========");
batch++;
this.sqlMapClient.update("receivableDataDetail.update", list.get(i));
if (batch%3000 == 0){
this.sqlMapClient.executeBatch();
this.sqlMapClient.startBatch();
}
}
this.logger.info("=============通用batchUpdate方法执行结束===========");
return this.sqlMapClient.executeBatch();
}
} catch (SQLException var4) {
var4.printStackTrace();
throw new RuntimeException(var4.getMessage(), var4);
}
}
做了如上操作后,生产导入的数据时间从4min提升到了30s
课外知识------------------------------------------------
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。每个线程使用的都是自己从内存中拷贝过来的变量的副本, 这样就不存在线程安全问题,也不会影响程序的执行性能。
ThreadLocal的接口方法:ThreadLocal类接口很简单,只有4个方法,ThreadLocal 可以存储任何类型的变量对象, get返回的是一个Object对象,但是我们可以通过泛型来制定存储对象的类型。
public T get() { } // 用来获取ThreadLocal在当前线程中保存的变量副本
public void set(T value) { } //set()用来设置当前线程中变量的副本
public void remove() { } //remove()用来移除当前线程中变量的副本
protected T initialValue() { } //initialValue()是一个protected方法,一般是用来在使用时进行重写的
有兴趣的可以看这个博主写的批量更新的源码,跟着他的思路慢慢看就能理顺