頻繁なオブジェクトの生成および破棄もパフォーマンスの劣化を引き起こす原因になります。バッチ処理を使用すればデータベース・サーバとの通信の回数を削減できますが、挿入したデータをすぐに更新したいなどの業務的な要件によりバッチ処理を使用できない場合、PreparedStatementの生成と破棄の回数が多くなって、オブジェクトの生成と破棄の繰り返しによるコストが無視できないものになってしまいます。
そこでPreparedStatementをキャッシュすることで、オブジェクトの生成と破棄の回数を減らす工夫をします。
import java.util.*; import java.sql.*; public class CreatePrepareStatement { // PreparedStatementを格納するキャッシュ private HashMap pstmtCache; private static CreatePrepareStatement instance; private Connection con; // Singletonパターンを使用するため // コンストラクタはprivateにする private CreatePrepareStatement() { pstmtCache = new HashMap(); } public synchronized static CreatePrepareStatement getInstance() { if(instance == null) { instance = new CreatePrepareStatement(); } return instance; } public PreparedStatement getPrepareStatement(String sqlQuery) { PreparedStatement pstmt = null; try{ // キャッシュ上にPreparedStatementが存在している場合 if(pstmtCache.containsKey(sqlQuery)) { // キャッシュより取得 pstmt = (PreparedStatement)pstmtCache.get(sqlQuery); } // キャッシュ上に存在しない場合は新規に生成し、 // キャッシュに格納する else { pstmt = con.prepareStatement(sqlQuery); pstmtCache.put(sqlQuery, pstmt); } } catch(SQLException se) { System.err.println(“SQL Failed”); se.printStackTrace(); } catch(Exception ex) { ex.printStackTrace(); } return pstmt; } public void SetConnection(Connection pCon) { this.con = pCon; } }
このCreatePrepareStatementクラスは単純な例として、1つのコネクションに対して1つのCreatePrepareStatementを作成することを想定しています。実際には大きなバッチ処理のプログラムを作成する際などに使用します。CreatePrepareStatementクラスのコンストラクタがprivateになっているのは、このクラスがコネクション1つにつき必ず1つだけ存在するSingletonパターン注を使用しているためです。Singletonパターンを使用すれば、コネクションがクローズされないかぎりPreparedStatementのキャッシュの効果はいつまでも持続します。
ただし、キャッシュのサイズが大きくなりすぎないように注意してください。キャッシュしたいSQL文の種類が多い場合や繰り返し呼ばれるSQL文の種類が少ない場合などは、キャッシュの恩恵を受けることができないだけではなく、キャッシュのサイズが大きくなりすぎてプログラムの性能に影響が出てしまうことがあります。
上述のように、要所にキャッシュの機能を実装することでシステムのパフォーマンスを向上させることができます。システムの設計の段階でシステムのボトルネックとなり得る部分をあらかじめ予想し、その部分にキャッシュ機能を持たせる設計をしていくとよいでしょう。
拡張性に優れたシステムにすればパフォーマンス・チューニングをする際の影響を最小限にとどめることができますので、デザインパターンを駆使してパフォーマンス面にも拡張面にも強い設計を心掛けてください。
全6回にわたって、「JavaのDBアクセスを極める」というテーマでJavaによるデータベース・アクセスの方法について連載してきました。オブジェクト指向モデルとデータベースモデルは、それぞれ設計手法としては一般的なものとして確立されてきていますが、それぞれの設計は目的とアプローチが異なっているためにインピーダンスミスマッチを引き起こしてしまいます(第1回 JavaとDBのデータモデルはナゼすれ違う?)。
そのためO/Rマッピングというフレームワークが必要になります。O/Rマッピングのフレームワークは、ビジネスロジック・レイヤとデータベース・レイヤを分け、それぞれのレイヤはほかのレイヤを意識しない構造にすることができるため、効率よくそれぞれのレイヤの実装をしていくことができるようになります(第2回 O/Rマッピングで失敗しない分析・設計のポイント)。
システムの設計の際には、インピーダンスミスマッチを引き起こす原因とそれにより引き起こされる問題について理解する必要があります。OOA(Object Oriented Analysis)とDOA(Data Oriented Approach)それぞれのアプローチの違いについての現実的な落としどころを探るためには、問題点を明確に把握したうえで設計段階で「永続化処理の分析・設計プロセス」を規定することが重要になります(第3回 OOAとDOAを併用した最適な分析/設計パターン)。
永続化の手段としてRDBだけではなく、OODB、XMLDBも視野に入れるべきでしょう。RDB/OODB/XMLDBそれぞれの特徴をよく理解して、システムに最適なDBMSを選択するようにするべきです(第4回 RDB/OODB/XMLDBで比較する永続化設計)。
OOAとDOAのインピーダンスミスマッチは、DAOパターンとFacadeパターンを使用することにより、ビジネスロジック・レイヤとデータベース・レイヤを切り離して考えられるようになります。こうすることで永続化処理の実装時における各レイヤ間の依存関係を最小限にできます(第5回 デザインパターンを利用したDBアクセスの実装)。
永続化処理を実装するうえでは、まずデザインパターンを用いてシステムの拡張に対する柔軟性やボトルネックとなり得る部分を考慮した設計を行い、実装においてもパフォーマンスを考慮して実装をしていきます(第6回 パフォーマンスを意識した実装テクニック)。
読者の皆さま、全6回にわたる当連載をお読みいただきありがとうございました。システムの構築を「なりわい」とし、日々、いろいろな課題に取り組んでいらっしゃる方々に、同じ立場の人間として、少しでも「書籍やベンダのマニュアルには記述されていない」「現場感のある」「もの作りの専門家としての」視点を執筆者一同、心掛けてきたつもりです。技術者の方々が「使用者の方々に喜んでいただけるシステム」を構築するための一助になれば幸いです。(連載完結)
アクセンチュア・テクノロジーソリューションズ
代表取締役社長
安間 裕
アクセンチュアから生まれた、企業改革のためのシステム開発を手掛けるエンジニア集団。安間裕が代表取締役社長を務める。諏訪勇紀はJavaに精通しSI上流での分析/設計を得意とするシニア・システム・アナリスト。
Copyright © ITmedia, Inc. All Rights Reserved.