进入batchUpdate源码
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);}}
我们关注
- ThreadLocal
- (1)this.getLocalSqlMapSession().startBatch(); -- 这里设置为true
- 此处的作用是在后续源码中,他会判断
if (statementScope.getSession().isInBatch()) {
- 如果为true ,就会把sql 放在一起List集合,再批量执行,否则直接执行!
- 此处的作用是在后续源码中,他会判断
- (2)this.getLocalSqlMapSession().update(sqlId, list.get(i)); 此处就会去做判断
- (3)this.getLocalSqlMapSession().executeBatch(); 开始批量更新
若想要优化批量更新操作,我们需要控制每次批量操作的数量,例如我遇到的生产环境需要导入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方法,一般是用来在使用时进行重写的
有兴趣的可以看这个博主写的批量更新的源码,跟着他的思路慢慢看就能理顺
ibatis 批量入库优化_getlocalsqlmapsession-CSDN博客