集計処理の実装
大分間が空いてしまいましたが、前回の
キーブレークを使った集計の実装を行ってみます
前々回の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