「薄い」JavaのO/Rマッパーの紹介 - DbUtils、Persist、Butterfly Persistence

HibernateActiveObjectsS2DaoApache CayenneiBATISなどORMフレームワークが群雄割拠状態なJavaですが、使い方を勉強したり設定ファイル書いたりするのが少し面倒かなと思っている人がいるかもしれません。
特にちょっとしたアプリケーションを作るならば、素のJDBCを使うのは嫌だけど、それに近い形で使えるORマッパーが欲しいと思うことがたびたびありました。
ということで以下の条件でJDBCを薄くラッピングしているJavaのライブラリを探して発見したものを紹介します。

  • レコードをオブジェクトに自動的にマッピングしてくれる
  • できるだけSQLを書く量を減らすことができる
  • いざとなったら生SQLを書くことができる
  • 導入が簡単である
  • 依存ライブラリが少ない

以下に挿入、検索、更新、削除を実行するコードを書いています。
今回はDBがMySQLだったので、MySQL Connector/J 5.1を使っています。
http://dev.mysql.com/downloads/connector/j/5.1.html
今回使用するUserクラスとuserテーブルの定義は一番最後に書いてあります。

DbUtils

http://commons.apache.org/dbutils/
DbUtilsApache Commonsプロジェクトの一つです。厳密に言うとDbUtilsはO/Rマッパーではない(と公式サイトで書いている)のですが、今回の要望に近いものなので取り上げました。
Connection、Statement、ResultSetを隠蔽してくれて、検索した結果をオブジェクトにマッピングしてくれます。
33KB程度のファイルサイズで、非常に軽いライブラリです。

import java.sql.Connection;
import java.sql.DriverManager;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;


public class DbUtilsTest {

	public static void main(String[] args) throws Exception {
		Class.forName("org.gjt.mm.mysql.Driver");
		String url = "jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8";
		Connection con = DriverManager.getConnection(url, "名前", "パスワード");

		User user = new User();
		user.setName("Kishi");
		QueryRunner qr = new QueryRunner();
		//挿入
		qr.update(con, "INSERT INTO user(name) VALUES(?)", user.getName());
		//検索
		ResultSetHandler h = new BeanListHandler(User.class);
		List<User> users = (List)qr.query(con, "SELECT * FROM user WHERE id=?", 1, h); 
		for(User u : users)
			System.out.println(u.getId() + ":" + u.getName());
		user = users.get(0);
		//更新
		qr.update(con, "UPDATE user SET name = ? WHERE id = ?", 
				new String[]{"Shiki", Integer.toString(user.getId())});
		//削除
		qr.update(con, "DELETE FROM user WHERE id = ?", user.getId());
		con.close();
	}
}

QueryRunnerのコンストラクタにDataSourceを渡すこともできます。
Handlerを切り替えることで、返ってくる値をMapやBean単体に変更することができます。
JDBCに比べると大分マシになりましたが、更新系の処理が少し面倒です。

Persist

http://code.google.com/p/persist/
Mr.Persisterというライブラリの情報を検索しているときに、「http://cappuccino.jp/keisuken/logbook/20081026.html」のコメントでmesoさんに紹介されているのを発見して知りました。
非常にシンプルなAPIなので、クイックスタートを見ればすぐに使い方がわかると思います。
何気にDbUtilsよりもファイルサイズが小さいですので、ソースコードに目を通すのが簡単です。
アノテーションジェネリクスを使っているのでJava5以降でしか使えません。

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;

import net.sf.persist.Persist;

public class PersistTest {
	public static void main(String[] args) throws Exception {
		Class.forName("org.gjt.mm.mysql.Driver");
		String url = "jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8";
		Connection con = DriverManager.getConnection(url, "名前", "パスワード");

		User user = new User();
		user.setName("Kishi");
		
		Persist persist = new Persist(con);
		//挿入
		persist.insert(user);
		//検索
		List<User> users = persist.readList(User.class,
				"select * from users where id= ? ",
				1);
		for(User u : users)
			System.out.println(u.getId() + ":" + u.getName());
		user = users.get(0);
		//更新
		user.setName("Shiki");
		persist.update(user);
		//削除
		persist.delete(user);

		con.close();
	}
}

更新系でテーブル名をしていませんが、Userクラスの場合だと、userあるいはusersテーブルを自動的に推測してくれます。@Tableアノテーションで直接指定することもできます。
依存ライブラリはないのですが、Log4Jを入れるとデバッグ情報を表示させることができます。
ジェネリクスをうまく使っているので戻り値をキャストする必要がないところや簡単な更新系ならばSQLを書く必要がないのがよいです。
APIもいけてるので、今回取り上げたものでDB簡単なプログラムを作るのであれば個人的にはおすすめです。

Butterfly Persistence

http://butterfly.jenkov.com/persistence/index.html
Butterfly Persistenceは先ほど名前が挙がったMr.Persisterの後継です。
上記二つよりも扱う範囲が非常に広く、コネクションプーリングやトランザクションを扱うこともできるようです。

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;

import com.jenkov.db.PersistenceManager;
import com.jenkov.db.itf.IDaos;


public class BetterflyPersitence {
	
	public static void main(String[] args) throws Exception{
		Class.forName("org.gjt.mm.mysql.Driver");
		String url = "jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8";
		Connection con = DriverManager.getConnection(url, "名前", "パスワード");

		User user = new User();
		user.setName("Kishi");PersistenceManager manager = new PersistenceManager();
		
		IDaos daos = manager.createDaos(con);
		//挿入
		daos.getObjectDao().insert(user);
		//検索
		List<User> users = daos.getObjectDao().readList(User.class, "select * from user where id = ?", 1);
		for(User u : users)
			System.out.println(u.getId() + ":" + u.getName());
		user = users.get(0);
		//更新
		daos.getObjectDao().update(user);
		//削除
		daos.getObjectDao().delete(user);

	}
}

IDaosから取得するDaoの種類によってMapにマッピングすることもできます。
Persistと同じくテーブル名を推測してくれます。
Webアプリケーションなどを作るのであれば、今回紹介したライブラリの中だとこれを使うのがベターだと思います。
日本語の情報が増えてくるともっと利用が増えそうです。

終わりに

どのライブラリも柔軟性が高く学習・導入コストもそんなにかからないので時間があったら是非試してみてください。


おまけ:Userクラスとテーブルの定義

public class User {
	private static final long serialVersionUID = 1L;
	private int id;
	private String name;

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
CREATE TABLE `user` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) default NULL,
  PRIMARY KEY  (`id`)
)