いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

iBatis奮闘記-0006 (バッチ処理)

大量のInsertなどが発行される場合、普通にひとつずつ発行していると
時間がかかってしまいます。そのようなときは、バッチ処理を行い
一括して処理を送信します。

iBatisにおいて、バッチ処理を行う場合、
以下のメソッドを使用します。

  • sqlMap.startBatch()
  • sqlMap.executeBatch()

startBatch()とexecuteBatch()の間でSQLを発行します。

以下、サンプルです。

[テーブル定義]

CREATE TABLE BIG_DATA_TABLE(
     id int
    ,val1 varchar(1000)
    ,val2 varchar(2000)
    ,val3 varchar(3000)
);

[テーブル対応クラス]

// vim:set ts=4 sw=4 et ws is nowrap ft=java fenc=cp932 ff=dos:
package gsf.samples.ibatis.sample005;

/**
 * BIG_DATA_TABLEテーブルに対応するドメインオブジェクト.<br/>
 *
 * @author gsf_zero1
 *
 */
public class BigData{
    
    /** ID */
    private Integer id;

    /** VAL1 */
    private String  val1;

    /** VAL2 */
    private String  val2;

    /** VAL3 */
    private String  val3;
    
    /**
     * Get id.
     *
     * @return id as Integer.
     */
    public Integer getId(){
        return this.id;
    }
    
    /**
     * Set id.
     *
     * @param id the value to set.
     */
    public void setId(Integer id){
        this.id = id;
    }
    
    /**
     * Get val1.
     *
     * @return val1 as String.
     */
    public String getVal1(){
        return this.val1;
    }
    
    /**
     * Set val1.
     *
     * @param val1 the value to set.
     */
    public void setVal1(String val1){
        this.val1 = val1;
    }
    
    /**
     * Get val2.
     *
     * @return val2 as String.
     */
    public String getVal2(){
        return this.val2;
    }
    
    /**
     * Set val2.
     *
     * @param val2 the value to set.
     */
    public void setVal2(String val2){
        this.val2 = val2;
    }
    
    /**
     * Get val3.
     *
     * @return val3 as String.
     */
    public String getVal3(){
        return this.val3;
    }
    
    /**
     * Set val3.
     *
     * @param val3 the value to set.
     */
    public void setVal3(String val3){
        this.val3 = val3;
    }

}

[SQL設定ファイル]

<?xml version="1.0" encoding="Windows-31J"?>
<!-- vim:set ts=4 sw=4 et ws is nowrap ft=xml fenc=cp932 ff=dos: -->
<!DOCTYPE sqlMap 
          PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" 
          "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="BatchSamples">

    <typeAlias alias="BigData" type="gsf.samples.ibatis.sample005.BigData"/>

    <select id="countBigData" resultClass="int">
        select
            count(id)
        from
            BIG_DATA_TABLE
    </select>

    <insert id="insertBigData" parameterClass="BigData">
        insert into BIG_DATA_TABLE
            (id, val1, val2, val3)
            values
            (#id#, #val1#, #val2#, #val3#)
    </insert>

    <delete id="allDeleteBigData">
        delete from BIG_DATA_TABLE
    </delete>

</sqlMap>

[サンプルクラス]

// vim:set ts=4 sw=4 et ws is nowrap ft=java fenc=cp932 ff=dos:
package gsf.samples.ibatis.sample005;

import java.util.*;

import org.apache.commons.lang.time.*;

import com.ibatis.common.resources.*;
import com.ibatis.sqlmap.client.*;

/**
 * Sample005の動作を確認するクラス.<br/>
 *
 * @author gsf_zero1
 *
 */
public class IBatisSample005{
    
    public static void main(String[] args) throws Exception{

        SqlMapClient sqlMap 
                        = SqlMapClientBuilder.buildSqlMapClient(
                                Resources.getResourceAsReader("SqlMapConfig.xml"));

        try{
            sqlMap.startTransaction();

            sqlMap.delete("allDeleteBigData", null);

            sqlMap.commitTransaction();
        }finally{
            sqlMap.endTransaction();
        }

        System.out.printf("BIG_DATA_TABLEの全データを削除......\n\n");

        //
        // 普通にinsertを60000件行う.
        //
        StopWatch stopWatch = new StopWatch();
        try{

            stopWatch.start();

            sqlMap.startTransaction();

            for(int i = 0; i < 60000; i++){
                BigData b = new BigData();

                b.setId*1;
                b.setVal1(String.format("%s-%d", "val1", (i + 1)));
                b.setVal2(String.format("%s-%d", "val2", (i + 1)));
                b.setVal3(String.format("%s-%d", "val3", (i + 1)));

                sqlMap.insert("insertBigData", b);
            }

            sqlMap.commitTransaction();

            stopWatch.stop();

        }finally{
            sqlMap.endTransaction();
        }

        System.out.printf("普通にInsert:%s\n\n", stopWatch);

        //
        // バッチ処理にて60000件Insert。
        //
        stopWatch = new StopWatch();
        try{

            stopWatch.start();

            sqlMap.startTransaction();

            //
            // バッチ処理開始.
            //
            sqlMap.startBatch();

            for(int i = 120000; i  > 60000; i--){
                BigData b = new BigData();

                b.setId*2;
                b.setVal1(String.format("%s-%d", "val1", (i + 1)));
                b.setVal2(String.format("%s-%d", "val2", (i + 1)));
                b.setVal3(String.format("%s-%d", "val3", (i + 1)));

                sqlMap.insert("insertBigData", b);
            }

            //
            // バッチ実行.
            //
            sqlMap.executeBatch();

            sqlMap.commitTransaction();

            stopWatch.stop();

        }finally{

            sqlMap.endTransaction();

        }

        System.out.printf("バッチ処理でInsert:%s\n\n", stopWatch);

        System.out.printf("Insert件数:%d\n", (Integer) sqlMap.queryForObject("countBigData", null));
    }
}

後は、iBatis設定ファイルにSQL設定ファイルを定義します。

    <sqlMap resource="gsf/samples/ibatis/sample005/Batch.ibatis.xml"/>

ローカルにて、H2 DatabaseをTCPモードでスタートさせて試してみましたが、大体以下のような結果になりました。
おおよそ、半分の時間ってところですね。
他の環境では、こうならないかもしれませんが。

exec:
     [java] BIG_DATA_TABLEの全データを削除......

     [java] 普通にInsert:0:00:27.219

     [java] バッチ処理でInsert:0:00:12.360

     [java] Insert件数:120000

ちなみに、H2 Databaseを組み込みモードで試すと、以下のようになりました。

exec:
     [java] BIG_DATA_TABLEの全データを削除......

     [java] 普通にInsert:0:00:08.031

     [java] バッチ処理でInsert:0:00:05.360

     [java] Insert件数:120000

*1:i + 1

*2:i + 1