2013年01月24日
設定ファイル無しで手軽・シンプルに使えるJava O/Rマッパー ICIQLの使い方
Java O/Rマッピング・フレームワークといえばiBATIS、Hibernate、S2JDBCあたりが昔から話題に上がりますが、細々した設定ファイル必要だったりと結局は手続き・設定が面倒になり、さんざん悩んだ挙句にApache/DbUtils(http://commons.apache.org/dbutils/)あたりに落ち着いたりします。
でも、やっぱり、ちょっとしたユーティリティを作るような時にはもう少しハイレベルなORMフレームワークを使って短時間でコーディングを終らせたい。
今日は設定ファイル不要・メソッド・チェーンで直感的にDBアクセス・レイヤーを記述できるICIQLフレームワークを使ってコーディングしてみます。
ICIQLの概要と準備
ICIQLライブラリの特徴は次の通り。
- Smallライブラリ(単一ファイルで完結、約125KBと小さなサイズ)
- ModelベースでデザインされたJDBCラッパー
- MySQL、HSQLDB、Derby、PostgreSQLデータベースに対応
スキーマの参照とアノテーションを用いることで最低限のコーディング量でデータにアクセスできるよう工夫されています。
また、メソッド・チェーンとSQLライクなSyntaxを組み合わせることで、何をしているコードなのか直感(SQL)的に把握出来るようになります。
必要なファイルはiciql-x.x.x.jarファイルだけ(勿論JDBCドライバは別途必要)。プロジェクト・ページからダウンロード(執筆時点で最新バージョンは1.1)し、クラスパスに追加しましょう。
今回はMySQLにmydbデータベースとITEMSテーブルを作成して評価します。
適当なデータも入れておきましょうね。

CREATE TABLE ITEMS ( ID MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, NAME VARCHAR(255) NOT NULL, PRICE INTEGER NOT NULL DEFAULT 0, QTY INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(id) );
適当なデータも入れておきましょうね。
ICIQLの基本的な使い方、JDBCとのコード比較
まず、JDBCだけでitemsテーブルを全レコードを取得してみます。
public List<Item> selectAllItems() throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException { List<Item> items = new ArrayList<Item>(); Connection conn = null; ResultSet rs = null; Statement stmt = null; try { Class.forName("com.mysql.jdbc.Driver").newInstance(); conn = DriverManager.getConnection("jdbc:mysql://localhost/mydb", "root", ""); conn.setAutoCommit(false); stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT * from items"); while (rs.next()) { Item item = new Item(); item.setId(rs.getInt("id")); item.setName(rs.getString("name")); item.setPrice(rs.getInt("price")); item.setQty(rs.getInt("qty")); items.add(item); } conn.commit(); } finally { rs.close(); stmt.close(); conn.close(); } return items; }
コード中にあるItemクラス(Bean)は省略しますが、大体こんなコードになりますよね。
何が辛いって手続きの多さと検査例外の多さ・・・。 何処かででこいつらをcatchするコードを書くのかと思うと気分も滅入ります。
これをICIQLを使って書いてみます。
まずは、テーブルに相当するモデル・クラスを用意して、
import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQTable; @IQTable(name="items") public class Item { @IQColumn(primaryKey = true) public Integer id; @IQColumn(length = 254, trim = true) public String name; @IQColumn public Integer price; @IQColumn(name = "QTY") public Integer quantity; }
カラムに相当するフィールドには@IQColumnアノテーションを設定します。
@IQColumnアノテーション中ではPK、実際カラム名、値範囲、DEFAULT値などを定義出来ます(詳しくはこちら)。
※ICIQLはデータベースのスキーマを参照しており嘘を書くとエラーになるので注意しましょう。
これで次のコードで全レコードを参照出来ます。
public List<Item> selectAllItems() { Db mydb = Db.open("jdbc:mysql://localhost/mydb","root", ""); Item i = new Item(); List<Item> items = mydb.from(i).select(); return items; }
これは簡単で良いですね!、非検査例外(IciqlException)になるので、明示しない限りtry~catch文は必要ありませんし、何をしようとしているのかコードから直感的に判断出来ます。
ご想像の通り、メソッド・チェーンの最後がselect()になるのはこの時点でSQLが確定するからで、select()の代わりにtoSQL()を呼び出すと実行するSQLを確認することも出来ます。
where句を使った条件指定も簡単に書けますよ。
Db mydb = Db.open("jdbc:mysql://localhost/mydb","root", ""); Item i = new Item(); List<Item> items = mydb.from(i).where(i.price).atLeast(200).select(); for (Item item: items) { System.out.println(item.id+","+item.name+","+item.price+","+item.quantity); } mydb.close();
実行結果をみると、200円以上のアイテムだけが結果セットに含まれています。
1,りんご,300,102,みかん,200,10
INSERT、UPDATEはこうなります。
// single insert mydb.insert(new Item(...)); // single update mydb.update(new Item(...)); mydb.insertAll(new ArrayList<Item>( Arrays.asList(new Item(...), new Item(...)))); // batch update. mydb.updateAll(new ArrayList<Item>( Arrays.asList(new Item(...), new Item(...))));
JOINや複雑なSQLはどうするの?
結合するなら、結合後の表に相当するクラスを一つ用意します。基本はテーブルと同じ。
ここではITEMS.IDとITEM_IDで関係付いたORDERSテーブルがある前提で次のようなクラスを用意してみます。
@IQTable public class OrderItem { @IQColumn public Integer order_id; @IQColumn public Integer cust_id; @IQColumn public Integer item_id; @IQColumn public Integer num_orders; @IQColumn public String item_name; public OrderItem() { } public OrderItem(Integer order_id, Integer cust_id, Integer item_id, Integer num_orders, String item_name) { this.order_id = order_id; this.cust_id = cust_id; this.item_id = item_id; this.num_orders = num_orders; this.item_name = item_name; } }
実際にJOINする場合コードは次のようになります。
Db mydb = Db.open("jdbc:mysql://localhost/mydb", "root", ""); final Item i = new Item(); final Order o = new Order(); List<OrderItem> orderItems = mydb.from(i).innerJoin(o).on(i.id) .is(o.item_id).orderBy(o.quantity).select(new OrderItem() { { order_id = o.id; cust_id = o.cust_id; item_id = i.id; num_orders = o.quantity; item_name = i.name; } }); for (OrderItem oi : orderItems) { System.out.println(oi.order_id + "," + oi.cust_id + "," + oi.item_name + "," + oi.num_orders); } mydb.close();
さらに複雑な場合には、JDBCと同じくexecuteQuery, ResultSetパターンを使うことも出来ます。
Db mydb = Db.open("jdbc:mysql://localhost/mydb","root", ""); ResultSet rs = mydb.executeQuery("select * from * * *"); try { while (rs.next()) { /* code */ } } catch (SQLException e) { e.printStackTrace(); } finally { mydb.close(); }
トランザクション管理は?
本格的に使うならやっぱりトランザクション管理は必要。 ICIQLでは・・・無いんです。
えっ、ええええー! と僕も思いましたよ・・・。参照用途ぐらいにしか使えないかと思ったんですが、実はGitHubでトランザクション管理機能を追加したコードが公開されています。
こちらのコードを使うか、次期ICIQLバージョン(1.2以上)での正式実装を待つことになりそうです。
Javaデータアクセス実践講座 (DB Magazine SELECTION)
posted with amazlet at 13.01.24
松信 嘉範
翔泳社
売り上げランキング: 197,650
翔泳社
売り上げランキング: 197,650
JDBCによるJavaデータベースプログラミング 第2版
posted with amazlet at 13.01.24
ジョージ リース
オライリー・ジャパン
売り上げランキング: 496,668
オライリー・ジャパン
売り上げランキング: 496,668
この記事へのトラックバックURL
http://trackback.blogsys.jp/livedoor/netbuffalo/4350590