IBATIS batch用法探究

有的時候需要一次性執行大批量的SQL,而不是執行一條SQL向數據庫提交一次,那麼會

 

用到 IBATIS 的batch提交。

 

IBATIS的 batch提交也是基於 JDBC的batch功能。

 

  那麼我現來寫段代碼示範一下:

 

第一步,建立我的測試類。

其中 BabyDO,sexEnum 等類是自己寫的,並不重要,只要讓程序跑起來,讀者可以自己寫下。不多講了,主要講重點的幾個語句。

package com.yajun;

// import  很多JDK給的類

import com.yajun.dataobject.BabyDO;
import com.yajun.enumdemo.SexEnum;
import com.yajun.impl.BabyDAOImpl;

/**
 * 專門測試batch操作的
 * 
 * @author yajun
 * 
 */
public class BatchInsertTest {
	private BabyDAOImpl babyDAO = new BabyDAOImpl();

	public static void main(String[] args) throws SQLException {
		BatchInsertTest test = new BatchInsertTest();
		test.insertBaby();
	}

	// 批量插入嬰兒並且插入數據庫
	private void insertBaby() throws SQLException {
		List<BabyDO> babys = new ArrayList<BabyDO>();
		long start = System.currentTimeMillis();
		for (int i = 0; i < 10000; i++) {
			BabyDO baby = new BabyDO();
			baby.setName("烏鴉軍");
			baby.setSex(SexEnum.Male);
			baby.setBirthday(new Date());
			baby.setHobby("踢寂寞足球");
			baby.setAge(i);
			babys.add(baby);
		}
		int count = babyDAO.insertBatch(babys);
		long end = System.currentTimeMillis();

		System.out.println("============ 成功插入BABY " + count + "條 =============");
		System.out.println("消費時間 " + (end - start));
	}
}

 

第二步,建立BabyDAO的實現類BabyDAOImpl

package com.yajun.impl;

// import  很多JDK給的類

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
import com.yajun.dao.BabyDAO;
import com.yajun.dataobject.BabyDO;
import com.yajun.dataobject.BabyTestDO;

public class BabyDAOImpl implements BabyDAO {
	private SqlMapClient client;

	public BabyDAOImpl() {
		try {
			Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
			client = SqlMapClientBuilder.buildSqlMapClient(reader);
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

  ……(省略其他方法)
	/**
	 * 批量添加小孩
	 * 
	 * @param babys
	 * @return
	 * @throws SQLException
	 */
	public Integer insertBatch(List<BabyDO> babys) throws SQLException {
		int count = 0;
		try {
                      // 自己控制事物的開始
			client.startTransaction();
			client.startBatch();
			for (BabyDO babyDO : babys) {
				try {
					client.insert("insert-baby", babyDO);
				} catch (Exception e) {
					System.out.println("插入嬰兒失敗");
				}

			}
			count = client.executeBatch();
		} finally {
                       // 自己控制事物的結束
			client.endTransaction();
		}
		return count;

	}

	……(省略其他方法)
 

運行起來的結果:

============ 成功插入BABY 10000條 =============
消費時間 2610

 

可以看到插入10000條數據只需要2秒鐘。

 

那麼這裏的關鍵點是我的註釋:需要手工控制事物執行,否則事物會在第一條SQL insert 的時候自動將事物關閉掉。

具體原因的代碼請看 IBATIS 源碼(主要請注意我加註釋的那幾句話):

public Object insert(SessionScope sessionScope, String id, Object param) throws SQLException {
    Object generatedKey = null;

    MappedStatement ms = getMappedStatement(id);
    Transaction trans = getTransaction(sessionScope);
// 如果trans爲空的華autoStart=true;如果不手工控制事物,那麼這裏就是爲空
    boolean autoStart = trans == null;

    try {
// 如果autoStart=true,開啓事物
      trans = autoStartTransaction(sessionScope, autoStart, trans);

      SelectKeyStatement selectKeyStatement = null;
      if (ms instanceof InsertStatement) {
        selectKeyStatement = ((InsertStatement) ms).getSelectKeyStatement();
      }

      // Here we get the old value for the key property. We'll want it later if for some reason the
      // insert fails.
      Object oldKeyValue = null;
      String keyProperty = null;
      boolean resetKeyValueOnFailure = false;
      if (selectKeyStatement != null && !selectKeyStatement.isRunAfterSQL()) {
        keyProperty = selectKeyStatement.getKeyProperty();
        oldKeyValue = PROBE.getObject(param, keyProperty);
        generatedKey = executeSelectKey(sessionScope, trans, ms, param);
        resetKeyValueOnFailure = true;
      }

      StatementScope statementScope = beginStatementScope(sessionScope, ms);
      try {
        ms.executeUpdate(statementScope, trans, param);
      }catch (SQLException e){
        // uh-oh, the insert failed, so if we set the reset flag earlier, we'll put the old value
        // back...
        if(resetKeyValueOnFailure) PROBE.setObject(param, keyProperty, oldKeyValue);
        // ...and still throw the exception.
        throw e;
      } finally {
        endStatementScope(statementScope);
      }

      if (selectKeyStatement != null && selectKeyStatement.isRunAfterSQL()) {
        generatedKey = executeSelectKey(sessionScope, trans, ms, param);
      }

// 如果autoStart爲true自動提交事務(提交事務以後,後面的SQL就沒有在一個BATCH裏面了)
      autoCommitTransaction(sessionScope, autoStart);
    } finally {
      autoEndTransaction(sessionScope, autoStart);
    }

    return generatedKey;
  }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章