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