FetchHandlerでメモリ対策 | ひぐかつのブログ

ひぐかつのブログ

ブログの説明を入力します。

例えば、データを30万件取得して、そのデータをループで回し、

データを加工して登録処理などを行う場合、OutOfMemoryになることは容易に考えられます。

今回、仕事上でそのような機会に出くわしたので、対応方法をメモしたいと思います。

前職の最後の現場でも同様の対応をした経験があったので、

あっさり対応することが出来ました。

s2daoのFetchHandlerです。

http://s2dao.seasar.org/ja/s2dao-tiger.html#FetchHandler

本来であれば、上記のように一度の30万件取得してから違う処理を行うという流れになりますが、

FetchHandlerを使用すると、

1件取得(Fetch)するたびに処理が行えるようになります。

つまり、30万件をメモリ上に持つ必要がなくなるわけです。

では使い方。

--------------------------------------------------------------------------------------------
前提条件:バージョン1.0.48以上であること。

①s2daoをダウンロードしてzipを解凍すると、s2dao, s2dao-tigerが入っているので、
s2dao-tiger/libのhsqldb.jar以外の2つのjarをクラスパスに通します。
(最初これがないのに気付かず「そんなクラスねぇよ」と怒られていました)

②FetchHandlerをimplementsしたhandlerクラスを作成します。

③DAOクラスの該当メソッドの引数の最後に②で作成したクラスを加えます。

④上記メソッドの戻り値をintにします(取得件数を返すようになります)

⑤この状態でDAOメソッドを実行すると、FetchHandlerクラスのexecuteメソッドを実行してくれます。

--------------------------------------------------------------------------------------------

では、「取得した年齢が28歳以上の場合、アラサーフラグを立ててデータを更新する」という、

全くもって今思いついた処理を、Fetchを利用しない場合、した場合で処理を書いてみようと思います。

■Fetchしない場合

List ageDtos = dao.getAge();

for (AgeDto ageDto : ageDtos) {
if (ageDto.getAge() >= 28) {
ageDto.setAround30Flg(true);

// 更新
dao.updateAgeInfo(ageDto);
}
}

■Fetchする場合
●AgeHandler.java

import org.seasar.dao.tiger.FetchHandler;

public class AgeHandlerimplements FetchHandler {

public boolean execute(AgeDto dto) {
if (ageDto.getAge() >= 28) {
ageDto.setAround30Flg(true);

// 更新
dao.updateAgeInfo(ageDto);
}
// Fetch処理を続ける(やめるときはfalseを返す)
return true;
}
}

●AgeDao.java

-- 中略 --

int getAge(AgeHandler handler);

●呼び出しクラス

// Handlerクラス生成
AgeHandler handler = new AgeHandler();

int executeCount = dao.getAge(handler);

といった感じになります。

ちなみにHandlerクラスのコンストラクタを用意してあげて、

引数にHandler内で保持させたい情報などを渡すと色々なことが出来ます。

今回、30万件引っ張ってきて、ファイル出力用にデータを編集して、30万件を一度にファイル出力する、

といった処理を行った際の対応だったので、

コンストラクタにはファイル出力クラスを渡してあげて、

Handlerクラス内で引き回し、1000件たまったらファイル出力する、

といった処理に変えてあげました。

2時間以上かかっていた処理が、1時間半弱まで短縮することが出来ました。

これで部長にアピールできたかなw