HibernateはHibernate Projectで開発されているオープンソースのO/Rマッピングツールで、軽量、柔軟、高機能ということで最近特に注目されています。
ということで、DBぷろふぇっしょなるとしては無視できないので、ちょっと勉強してみます。
Hibernateの特徴
Hibernateの特徴には以下のような特徴があります。- シンプル
- POJO(Plain Old Java Object)と呼ばれる普通のJava Beansを永続化するため、簡単に利用できます。
- 高性能
- cglibと呼ばれるライブラリを利用して従来のO/Rマッピングツールよりも高速にアクセスでき、また、キャッシュ、遅延読み込み、コネクションプーリングといった高速化のための機能もサポートしています。
- 柔軟なマッピング
- 1:n、n:1、n:nなどの多様なリレーションや継承関係に対応しています。また、HQLと呼ばれるクエリー言語を利用することにより、動的でより柔軟性の高いマッピングが可能です。
- 高機能
- 主キーの自動生成機能、遅延ロード機能、キャッシュ機能、外部結合サポートなど、O/Rマッピングで必要とされる機能はほとんど揃っています。
- 豊富なドキュメントと関連ツール
- Hibernateのマニュアルや技術情報はHibernate Projectのサイト から簡単に入手することができます。マニュアルについては日本語版 も用意されており、その他市販の解説書も何冊か出版されています。また、Hibernate Extensions やMiddlegen など、Hibernateをサポートするツールも数多く出回っています。
Hibernateの仕組み
Hibernateを構成する主な要素は、Java Beans、マッピングファイル、Hibernate設定ファイル、データベースの表定義の4つです。- Java Beans
- データベースの表定義に対応したGetter/Setterメソッドを持つPOJOクラス。このJava Beansを利用してデータが永続化されます。
- マッピングファイル
- データベースの表とJava Beans、列とクラス属性を対応づけるXMLファイル。その他、主キーの生成方法や、リレーションなど様々な設定情報を記述できます。
- Hibernate設定ファイル
- データベースへの接続情報やHibernateの基本的な動作を設定するファイル。XML(hibernate.cfg.xml)もしくはプロパティファイル(hibernate.properties)として定義します。
- データベースの表定義
- データを永続化するRDBMS上の表。商用、オープンソースとも、主要なRDBMSにはほとんど対応しています。
Hibernateを利用した簡単なO/Rマッピング例
OracleのSCOTT.DEPT表にマッピングする場合、SCOTT.DEPT DEPTNO NUMBER(2,0) NOT NULL, DNAME VARCHAR2(14), LOC VARCHAR2(13)以下のようなJava Beanを用意し、
以下のようなJava Beanを用意し、
Dept.java
package scott; import java.io.Serializable; import org.apache.commons.lang.builder.ToStringBuilder; /** * @hibernate.class * table="DEPT" * */ public class Dept implements Serializable { /** identifier field */ private Integer deptno; /** nullable persistent field */ private String dname; /** nullable persistent field */ private String loc; /** full constructor */ public Dept(Integer deptno, String dname, String loc) { this.deptno = deptno; this.dname = dname; this.loc = loc; } /** default constructor */ public Dept() { } /** minimal constructor */ public Dept(Integer deptno) { this.deptno = deptno; } /** * @hibernate.id * generator-class="assigned" * type="int" * column="DEPTNO" * */ public Integer getDeptno() { return this.deptno; } public void setDeptno(Integer deptno) { this.deptno = deptno; } /** * @hibernate.property * column="DNAME" * length="14" * */ public String getDname() { return this.dname; } public void setDname(String dname) { this.dname = dname; } /** * @hibernate.property * column="LOC" * length="13" * */ public String getLoc() { return this.loc; } public void setLoc(String loc) { this.loc = loc; } public String toString() { return new ToStringBuilder(this) .append("deptno", getDeptno()) .toString(); } }このJava Beanと表のマッピングを定義し、
Dept.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" > <hibernate-mapping> <!-- Created by the Middlegen Hibernate plugin 2.1 http://boss.bekk.no/boss/middlegen/ http://www.hibernate.org/ --> <class name="scott.Dept" table="DEPT" > <meta attribute="class-description" inherit="false"> @hibernate.class table="DEPT" </meta> <id name="deptno" type="int" column="DEPTNO" > <meta attribute="field-description"> @hibernate.id generator-class="assigned" type="int" column="DEPTNO" </meta> <generator class="assigned" /> </id> <property name="dname" type="java.lang.String" column="DNAME" length="14" > <meta attribute="field-description"> @hibernate.property column="DNAME" length="14" </meta> </property> <property name="loc" type="java.lang.String" column="LOC" length="13" > <meta attribute="field-description"> @hibernate.property column="LOC" length="13" </meta> </property> <!-- Associations --> </class> </hibernate-mapping>データベース接続情報などを記述した設定ファイルを用意します。
hibernate.cfg.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property> <property name="connection.url">jdbc:oracle:thin:@oraserver:1521:ORCL</property> <property name="connection.username">scott</property> <property name="connection.password">tiger</property> <property name="dialect">net.sf.hibernate.dialect.Oracle9Dialect</property> <property name="show_sql">false</property> <mapping resource="scott/Dept.hbm.xml"/> </session-factory> </hibernate-configuration>これらを利用して実際にSCOTT.DEPT表にアクセスするプログラムは以下のようになります。
SampleDept.java
package scott; import net.sf.hibernate.HibernateException; import net.sf.hibernate.cfg.Configuration; import net.sf.hibernate.Session; import net.sf.hibernate.SessionFactory; import net.sf.hibernate.Transaction; public class DeptCRUD { public static void main(String[] args) throws HibernateException { Configuration cfg = new Configuration(); cfg.configure(); SessionFactory factory = cfg.buildSessionFactory(); Session s = factory.openSession(); Dept dept = null; System.out.println("------ 登録 ------"); Transaction tx = s.beginTransaction(); try{ dept = new Dept(); dept.setDeptno(new Integer(99)); dept.setDname("Develop"); dept.setLoc("Tokyo"); s.save(dept); tx.commit(); } catch (HibernateException e ) { tx.rollback(); s.close(); return; } s.clear(); try{ System.out.println("------ 読込 ------"); dept = (Dept) s.load(Dept.class, new Integer(99)); System.out.println("DEPTNO:"+dept.getDeptno()); System.out.println("DNAME:"+dept.getDname()); System.out.println("LOC:"+dept.getLoc()); System.out.println("------ 更新 ------"); dept.setLoc("Osaka"); tx.commit(); } catch (HibernateException e ) { tx.rollback(); s.close(); return; } s.clear(); try{ System.out.println("------ 再読込 ------"); dept = (Dept) s.load(Dept.class, new Integer(99)); System.out.println("DEPTNO:"+dept.getDeptno()); System.out.println("DNAME:"+dept.getDname()); System.out.println("LOC:"+dept.getLoc()); System.out.println("------ 削除 ------"); s.delete(dept); tx.commit(); } catch (HibernateException e ) { tx.rollback(); } finally { s.close(); } } }見ての通り非常にシンプルなプログラムとなります。
Java Beans、マッピングファイル、Hibernate設定ファイル、データベースの表定義と用意するものは多いですが、Java Beans、マッピングファイル、データベースの表定義についてはツールを使えば、どれか1つからあとの2つを自動生成することができるため、それほど準備に手間が掛かるということはありません。
(上記の例ではMiddlegenを利用してデータベースの表定義からJava Beanとマッピング定義ファイルを自動生成しています。)
Hibernateを使用すべきシステムと使用すべきでないシステム
Hibernateはオブジェクト指向モデルとリレーショナルモデルのインピーダンスミスマッチを解消し、データベース上の永続データをオブジェクト指向的に簡単に扱うためのO/Rマッピングツールです。したがって、テーブル数が多く、かつ相互に複雑な関係を持つようなシステムにおいて効果を発揮します。
(テーブル数が少ないシステムであればわざわざHibernateを使うほどでもありません。)
ただし、Hibernateはテーブル内の1レコード、もしくはそれに関連するテーブル内の少数の関連レコードを扱う処理には向いていますが、大量のレコードを一括検索、一括更新するような処理には向いていません。
これはHibernateに限った問題ではないですが、このような処理でパフォーマンスが出ない場合には、マッピングの設定をチューニングする、もしくは、Hibernateで全てを処理するのではなく、Hibernateに適さない処理の部分をSQLやストアドプロシージャを使って処理をしたほうがいいでしょう。
DAOパターンをうまく活用すれば、Hibernateに適した処理はHibernateで、Hibernateに適さない処理はHibernateのSession内からJDBCを呼び出し、SQL文を直接記述する、またはストアドプロシージャを呼び出すという形で実装し、実装の違いをDAOクラス内に隠蔽することも可能です。
まとめ
Hibernateを使えばO/Rマッピングを比較的シンプルに実装することができます。
豊富な機能を使いこなせば、JDBCではかなり難しいコーディングを強いられるような高度なデータアクセスも実現可能だし、Hibernateに適さない大量レコードの一括処理などについてはJDBCを直接利用してアクセスすることもできます。
ということで、使い方次第で様々なシステム形態、データモデルに柔軟に対応できそうです。
現状では、最も強力で魅力的なO/Rマッピングツールと言えるのではないでしょうか。