zl程序教程

您现在的位置是:首页 >  其他

当前栏目

iBatis batch处理那些事详解编程语言

编程语言 详解 处理 那些 Batch ibatis
2023-06-13 09:20:40 时间
Java代码 
收藏代码 public Object batchExecute(final CallBack callBack) {   Object result = getSqlMapClientTemplate().execute(new SqlMapClientCallback Object () {     @Override   public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {   executor.startBatch();   Object obj = callBack.execute(new IbatisSqlExecutor(executor));   executor.executeBatch();   return obj;   }   });   return result;  } 



不想将SqlMapExecutor侵入到业务代码中,于是又有了如下3个类,在今天的主题中不是关键,可以忽略,只是为了将代码贴完整 


Java代码 
收藏代码 public interface SqlExecutor {   Object insert(String id, Object parameterObject) throws SQLException;   Object insert(String id) throws SQLException;   int update(String id, Object parameterObject) throws SQLException;   int update(String id) throws SQLException;   int delete(String id, Object parameterObject) throws SQLException;   int delete(String id) throws SQLException;   Object queryForObject(String id, Object parameterObject) throws SQLException;   Object queryForObject(String id) throws SQLException;   Object queryForObject(String id, Object parameterObject, Object resultObject) throws SQLException;  } 
Java代码 
收藏代码 class IbatisSqlExecutor implements SqlExecutor {   private SqlMapExecutor executor;   IbatisSqlExecutor(SqlMapExecutor executor) {   this.executor = executor;   }   @Override   public Object insert(String id, Object parameterObject) throws SQLException {   return executor.insert(id, parameterObject);   }   // 剩下的就省略了,和insert都类似  } 
Java代码 
收藏代码 public interface CallBack {   Object execute(SqlExecutor executor);  } 
Java代码 
收藏代码 getDao().batchExecute(new CallBack() {   @Override   public Object execute(SqlExecutor executor) {   for (int i = 0; i   10000; ++i) {   // 注意每个sql_id的sql语句都是不相同的   executor.insert( id1 , obj1);   executor.insert( id2 , obj2);   //     executor.insert( idn , objn);   }   return null;   }  }); 



再然后 尼玛不但速度没变快还异常了,原因竟然是生成了太多的PreparedStatement,你妹不是批处理吗?不是应该一个sql只有一个PreparedStatement吗?
 


跟踪iBatis代码,发现了iBatis是这样处理的 


Java代码 
收藏代码 // 以下代码来自com.ibatis.sqlmap.engine.execution.SqlExecutor$Batch     public void addBatch(StatementScope statementScope, Connection conn, String sql, Object[] parameters) throws SQLException {   PreparedStatement ps = null;  // 就是它:currentSql   if (currentSql != null   currentSql.equals(sql)) {   int last = statementList.size()   1;   ps = (PreparedStatement) statementList.get(last);   } else {   ps = prepareStatement(statementScope.getSession(), conn, sql);   setStatementTimeout(statementScope.getStatement(), ps);  // 就是它:currentSql   currentSql = sql;   statementList.add(ps);   batchResultList.add(new BatchResult(statementScope.getStatement().getId(), sql));   }   statementScope.getParameterMap().setParameters(statementScope, ps, parameters);   ps.addBatch();   size++;   } 



不细解释了,只看currentSql这个实例变量就知道了,如果sql与前一次不同那么会新建一个PreparedStatement,所以刚才的伪代码应该这样写: 


Java代码 
收藏代码 getDao().batchExecute(new CallBack() {   @Override   public Object execute(SqlExecutor executor) {   for (int i = 0; i   10000; ++i) {   executor.insert( id1 , obj1);   }   for (int i = 0; i   10000; ++i) {   executor.insert( id2 , obj2);   }   //  你就反复写for循环吧   return null;   }  }); 



很不爽是不?反正我是决了一个定,改iBatis的源码 


改源码最好这么来: 


1.复制对应类的源码到工程下,本例中要复制的是com.ibatis.sqlmap.engine.execution.SqlExecutor 


注意要保持包名,其实是类的全限定名称要一样哇,这样根据ClassLoader的类加载机制会先加载你工程中的SqlExecutor而不加载iBatis jar包中的对应SqlExecutor 


如图: 




iBatis batch处理那些事详解编程语言
 




2.改之,只改static class Batch这个内部类即可,策略是去掉currentSql,将PreparedStatement放入HashMap 


如下: 


Java代码 
收藏代码 public void addBatch(StatementScope statementScope, Connection conn, String sql, Object[] parameters) throws SQLException {   PreparedStatement ps = statementMap.get(sql);   if (ps == null) {   ps = prepareStatement(statementScope.getSession(), conn, sql);   setStatementTimeout(statementScope.getStatement(), ps);   statementMap.put(sql, ps);   batchResultMap.put(sql, new BatchResult(statementScope.getStatement().getId(), sql));   }   statementScope.getParameterMap().setParameters(statementScope, ps, parameters);   ps.addBatch();   ++size;   } 





下面贴出修改后完整的代码,方便有同样需求的同学修改,只贴出内部类com.ibatis.sqlmap.engine.execution.SqlExecutor$Batch,com.ibatis.sqlmap.engine.execution.SqlExecutor没有做出任何修改 


Java代码 
收藏代码 private static class Batch {     private Map String, PreparedStatement  statementMap = new HashMap String, PreparedStatement    private Map String, BatchResult  batchResultMap = new HashMap String, BatchResult    private int size;     /**   * Create a new batch   */   public Batch() {   size = 0;   }     /**   * Getter for the batch size   * @return   the batch size   */   @SuppressWarnings( unused )   public int getSize() {   return size;   }     /**   * Add a prepared statement to the batch   * @param statementScope   the request scope   * @param conn   the database connection   * @param sql   the SQL to add   * @param parameters   the parameters for the SQL   * @throws SQLException   if the prepare for the SQL fails   */   public void addBatch(StatementScope statementScope, Connection conn, String sql, Object[] parameters) throws SQLException {   PreparedStatement ps = statementMap.get(sql);   if (ps == null) {   ps = prepareStatement(statementScope.getSession(), conn, sql);   setStatementTimeout(statementScope.getStatement(), ps);   statementMap.put(sql, ps);   batchResultMap.put(sql, new BatchResult(statementScope.getStatement().getId(), sql));   }   statementScope.getParameterMap().setParameters(statementScope, ps, parameters);   ps.addBatch();   ++size;   }     /**   * TODO (Jeff Butler)   maybe this method should be deprecated in some release,   * and then removed in some even later release. executeBatchDetailed gives   * much more complete information.   *  p/    * Execute the current session s batch   * @return   the number of rows updated   * @throws SQLException   if the batch fails   */   public int executeBatch() throws SQLException {   int totalRowCount = 0;   for (Map.Entry String, PreparedStatement  iter : statementMap.entrySet()) {   PreparedStatement ps = iter.getValue();   int[] rowCounts = ps.executeBatch();   for (int j = 0; j   rowCounts.length; j++) {   if (rowCounts[j] == Statement.SUCCESS_NO_INFO) {   // do nothing   } else if (rowCounts[j] == Statement.EXECUTE_FAILED) {   throw new SQLException( The batched statement at index   + j +   failed to execute. );   } else {   totalRowCount += rowCounts[j];   }   }   }   return totalRowCount;   }     /**   * Batch execution method that returns all the information   * the driver has to offer.   * @return a List of BatchResult objects   * @throws BatchException (an SQLException sub class) if any nested   * batch fails   * @throws SQLException if a database access error occurs, or the drive   * does not support batch statements   * @throws BatchException if the driver throws BatchUpdateException   */   @SuppressWarnings({  rawtypes ,  unchecked  })   public List executeBatchDetailed() throws SQLException, BatchException {   List answer = new ArrayList();   int i = 0;   for (String sql : statementMap.keySet()) {   BatchResult br = batchResultMap.get(sql);   PreparedStatement ps = statementMap.get(sql);   try {   br.setUpdateCounts(ps.executeBatch());   } catch (BatchUpdateException e) {   StringBuffer message = new StringBuffer();   message.append( Sub batch number  );   message.append(i + 1);   message.append(  failed. );   if (i   0) {   message.append(   );   message.append(i);   message.append(  prior sub batch(s) completed successfully, but will be rolled back. );   }   throw new BatchException(message.toString(), e, answer, br.getStatementId(), br.getSql());   }   ++i;   answer.add(br);   }   return answer;   }     /**   * Close all the statements in the batch and clear all the statements   * @param sessionScope   */   public void cleanupBatch(SessionScope sessionScope) {   for (Map.Entry String, PreparedStatement  iter : statementMap.entrySet()) {   PreparedStatement ps = iter.getValue();   closeStatement(sessionScope, ps);   }   statementMap.clear();   batchResultMap.clear();   size = 0;   }  }  

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/13584.html

cjava