Challenge Engineer Life !

エンジニア人生を楽しみたい!仕事や趣味で学んだ技術的なことを書いていくブログです。

JPAのINSERTでEclipseLinkのbatch-writingを試してみました

今日は開発メンバからJPAに関して

「XXテーブルに明細データとして保存時に50~100レコードくらい追加する処理があるんですけど、for文で回せばいいんすかね?」

と聞かれました。
そういえば金魚本には一括更新と一括削除はあったけど、一括INSERTってなかったような…と(^^;未だに知識が金魚本ベースな自分。

JSR 317のドキュメントも簡単にみてみましたが、やはりbulk updateとbulk deleteしか見当たらない…。記述があったらごめんなさい…(^^;

けれど「bulk」いうキーワードを得て「JPA bulk INSERT」とか「JPA batch INSERT」でググってみました。
が、あまり「これだ!」というのが見つからなかったです。

上記2つ目のページでEclipseLinkにbatch-writingなるプロパティがある、という情報があって、調べてみると、persistence.xmlで以下のような設定ができるとのこと。

<property name="eclipselink.jdbc.batch-writing" value="JDBC"/>
<property name="eclipselink.jdbc.batch-writing.size" value="1000"/>

これを確認してみることにしました。

batch-writing

それぞれの説明ドキュメントは以下にありました。が、質素な感じ…。

とりあえず検証のために簡単な名簿テーブルを作成して、以下のようなプログラムを試しました。

package jp.co.hoge.dbperformance;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

@Named(value = "meiboBean")
@RequestScoped
public class MeiboBean implements Serializable{

    @Inject
    MeiboFacade meiboFacade;
    
    public MeiboBean() {
    }
    
    public  void save(){
        Calendar calendarBefore = Calendar.getInstance();
        System.out.println(
                calendarBefore.get(Calendar.HOUR_OF_DAY) + "時" +
                calendarBefore.get(Calendar.MINUTE) + "分" +
                calendarBefore.get(Calendar.SECOND) + "秒");
        
        //50000件インサート
        for(int i = 0; i < 50000; i++){
            Meibo meibo = new Meibo();
            String id = Integer.toString(i);
            meibo.setId(id);
            meibo.setName("私の名前はななしのごんべい" + i);
            meibo.setAge(i);
            meibo.setAddress("とうきょうとかつしかくかめありこうえんまえ" + i);
            meibo.setBiko1("びこうなのでなんでもいいのです");
            meibo.setBiko2("びこうなのでてきとーてきとーてきとー");
            meibo.setBiko3("びこうなのでなにをいれてもいいんですの");
            meibo.setBirthday("1980/5/5");
            meibo.setCreateDate(new Date());
            meibo.setUpdateDate(new Date());
            meibo.setCreateUser("makimaki"+i);
            meibo.setUpdateUser("makimaki"+i);
            meiboFacade.create(meibo);
        }

        Calendar calendarAfter = Calendar.getInstance();
        System.out.println(
                calendarAfter.get(Calendar.HOUR_OF_DAY) + "時" +
                calendarAfter.get(Calendar.MINUTE) + "分" +
                calendarAfter.get(Calendar.SECOND) + "秒");
    }
}

MeiboFacadeはNetBeansの「エンティティ・クラスのセッションBean」で作ったEJBです。

動作確認の環境は自分の開発マシンのみで、DBもローカルのを使いました。

OS:Windows 7 Professional
APサーバ:GlassFish 3.1.2.2
DBサーバ:Microsoft SQL Server 2008 R2
OR:EclipseLink 2.3.2

って、ここ書いてて検証環境のEclipseLinkが古いことに気づきました…orz
が、とりあえず、やってしまったので、そのままに。

結果

理系ならちゃんと統計的にやれ…と大学時代の教授に怒られそうですが、ここでは1回きりの計測です…。すいません。そしてせめてミリ秒まで出せよ…とか色々突っ込み受けそうですが、半分は自分メモなので、ごめんなさい(^^;

条件 秒数
batch-writingなし 32秒
batch-writingあり size なし 34秒
batch-writingあり size 100 34秒
batch-writingあり size 1000 38秒

なんか微妙…ホントに設定が効いてるのか怪しいので、ログ出力した所

batch-writingありの場合

FINER: Begin batch statements
中略
FINER: End Batch Statements

batchと出てます。

batch-writingなしの場合

FINER: TX binding to tx mgr, status=STATUS_ACTIVE
FINER: acquire unit of work: 11429200
FINEST: persist() operation called on: jp.co.hoge.dbperformance.Meibo[ id=3 ].
FINER: TX beforeCompletion callback, status=STATUS_ACTIVE
FINER: begin unit of work commit
FINER: TX beginTransaction, status=STATUS_ACTIVE
FINEST: Execute query InsertObjectQuery(jp.co.hoge.dbperformance.Meibo[ id=3 ])
FINEST: Connection acquired from connection pool [default].
FINEST: reconnecting to external connection pool
中略
FINEST: Connection released to connection pool [default].
FINER: TX afterCompletion callback, status=COMMITTED
FINER: end unit of work commit
FINER: release unit of work
FINER: client released
FINER: client acquired: 1308253

いつも通りな感じです。

なので一応、設定は効いているのかな、と。

ちなみにこのFINESTを付けた状態での秒数は

条件 秒数
batch-writingなし 93秒
batch-writingあり size 1000 113秒

ほんまかいな…。

なんか自分の検証方法が間違ってる気がしてきました…。

まあ、今回は元々の開発メンバの質問的にあるように「50~100レコード」なので、batchつけなくてもいいかなと。
一括INSERTで良い方法ってあるんだろか~。

2013/12/7 追記
@makingさんから以下コメント頂きました。


上記サンプルだと管理Beanからfor文でEJBのDB処理を呼んでるので、普通に考えると1回ごとコミットされてそうです(^^;これを確認した当時は多分ログで「Begin/End batch statements」を確認して、バッチ処理されてると思い込んでたみたいですが、最後の性能をみても、やはり細かい検証がいりそうだと改めて思いました(いつ検証するかは未定ですが。。)

にほんブログ村 IT技術ブログへ
にほんブログ村 にほんブログ村 IT技術ブログ Javaへ
にほんブログ村