大分間が空いてしまいましたが、前回の
キーブレークを使った集計の実装を行ってみます
前々回のhibernateで単純なSELECT文の順読み込み
のAppクラスに変更を加えました
実装の主な要点は
- ブレークキーや集計項目の割当の場所
- 最後の1件の出力を忘れないこと
です
下が実装です
App.java
package package com.example.exp1;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* Hello world!
*
*/
public class App
{
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static void main( String[] args )
{
SessionFactory f = buildSessionFactory();
Session ss = f.openSession();
ss.beginTransaction();
ScrollableResults rs = ss.createSQLQuery("select * from URIAGE_CHOU order by NENGETSU")
.addEntity(UriageChou.class)
.scroll(ScrollMode.FORWARD_ONLY)
;
// 読み込んだレコード件数。ループ後の最後の1件の出力で使います
int recordCount = 0;
// 集計用変数
String nengetsuSum = null;
int gakuSum = 0;
while(rs.next()) {
++recordCount;
UriageChou u = (UriageChou) rs.get(0);
// ブレークしたら集計結果を出力します
if(breaks(u.getNengetsu(), nengetsuSum)) {
// 集計結果を出力
System.out.println(nengetsuSum + ", " + Integer.toString(gakuSum));
// 集計用変数を初期化
gakuSum = 0;
}
nengetsuSum = u.getNengetsu();
gakuSum += u.getGaku().intValue();
}
// ループする対象のレコードが無くなり、集計用の変数にの残っている最後の集計結果を出力する
if(recordCount > 0) {
// 集計結果を出力
System.out.println(nengetsuSum + ", " + Integer.toString(gakuSum));
}
if(recordCount == 0) {
System.out.println("集計対象のレコードがありませんでした");
}
ss.getTransaction().commit();
}
/**
* ブレークしたかを判定します
* @param nengetsuComing 読み込んだレコードのブレークキー
* @param nengetsuSum 集計中のブレークキー
* @return trueならブレークしている
*/
public static boolean breaks(String nengetsuComing, String nengetsuSum) {
if(nengetsuComing == null) {
throw new IllegalArgumentException();
}
// 1レコード目をブレークと見なさないために、
// nengetsuSumがnullの場合は、ブレークでないものとする
if(nengetsuSum == null) {
return false;
}
boolean result = false == nengetsuSum.equals(nengetsuComing);
return result;
}
}
実行結果
Hibernate: select * from URIAGE_CHOU order by NENGETSU 201305, 100 201306, 130
このような集計処理の、何が嬉しいのか
このようキーブレークによる集計処理が、どんな場面で有用なのかを考えてみます
全ての集計結果を保持すると、大量のメモリを消費する場面
下の参考で言及されているMapを使った実装方法では
全ての集計結果を、集計が終わるまで、メモリに持ちます
キーブレークを使った実装では、集計ができるごとに、出力することができます
そのときにファイルやデータベースへ保存すれば、集計結果をメモリに持つ必要がありません
例えばもし、全ての集計結果をメモリへ持ったときの、
メモリ使用量が、100MBとか大量のメモリを消費するような
状況が起こったします
そうすると、その集計処理や、同時に実行されている他の処理が、
不安定になるようなことも、あるでしょう
集計が複雑な場面
この記事の例に上げた集計では、SQL文で書けるようなものです
現実世界の集計では、
- 集計元が、幾つにも分かれている
- 集計条件が、SQL文で書けないくらい複雑
というような状況があります
おわりに
今回は実務で、キーブレークによる集計処理が、
有用な場面に自分が遭遇したので、記事にしようと、思い立ちました
何かの参考になればと思います
参考させて頂いた記事
下の記事を参考にさせて頂きました
ありがとうございます
キー毎に値を集計する方法
http://blog.goo.ne.jp/hishidama/e/0c0b4097643ad7d4f737cca79731587c
リストを項目ごとに集計する
http://d.hatena.ne.jp/irof/20111203/p1




