Hibernate入門


- 第1回 Hibernate で作る HelloWorld アプリケーション -

目次

はじめに

数あるO/Rマッピングツールの中でも、最も人気がある Hibernate について、実際に O/R マッピングを試しながら特徴や効果的な使い方を調査して行こうと思います。Hibernate は JBoss に吸収され、特に JBoss において EJB3.0 を実現する技術として重要な存在になっています。

Hibernate は EJB3.0 の仕様策定にも大きな影響を与えていることから、今後の EJB や SOA との関わりを考えると知っておいて損は無い技術でしょう。

今回は第1回ということで、Hibernate を使った非常にシンプルな HelloWorld アプリケーションを作成してみます。

HSQLDBのインストール

インストール

データベースとして、HSQLDBを使用します。Webサイトから最新版の媒体(zipファイル)をダウンロードして、適当なディレクトリに展開します。尚、筆者は執筆時点(2006/10/24)での最新版の 1.8.0.7 で動作確認しています。

バッチファイルの作成

Windows向けのバッチファイルは提供されていないため、以下の1行からなるファイル(runServer.bat)を %HSQLDB_HOME%/bin に作成します。

[runServer.bat]

@java -classpath ../lib/hsqldb.jar org.hsqldb.Server

またHSQLDBのデータベース・マネージャ(GUIクライアント)を起動するバッチファイルも作成します。以下の2行からなるファイル(runManager.bat)を %HSQLDB_HOME%/bin に作成します。

[runManager.bat]

cd ..\data
@java -cp ..\lib\hsqldb.jar org.hsqldb.util.DatabaseManager

DBスキーマの作成

%HSQLDB_HOME%/bin に server.properties という名前のファイルを作成し、以下のように記述します。

[server.properties]

#server.port=9002
#server.silent=false

server.database.0=sample
server.dbname.0=sample

データベース"sample"が生成されます。HSQLDBのデフォルト・ポート番号は9001ですが、これを変更したい場合は server.port プロパティを有効にします。HSQLDBのコンソールに様々なメッセージを出力させたい場合は、server.silent=false を有効にします。

動作確認

runServer.bat を実行し、コンソールに以下のように出力されればインストール完了です。

終了させる場合は、コンソール上で Ctrl + C を入力します。

Ant のインストール

http://ant.apache.org/ から Ant の媒体をダウンロードし、適当なディレクトリに展開します。尚、筆者は執筆時点(2006/10/24)での最新版の 1.6.5 で動作確認しています。展開後、環境変数 ANT_HOME を作成し、%ANT_HOME%/bin にパスを通します。

Hibernate のインストール

http://www.hibernate.org/ から Hibernate の媒体をダウンロードします。今ところダウンロードするのは、Hibernate Core のみでOKです。ダウンロード後、適当なディレクトリに展開します。筆者は執筆時点(2006/10/24)での最新版の Hibernate Core 3.2.0 GA で動作確認しています。

Eclipse のインストール

開発環境には Eclipse を使うことにします。Eclipse のバージョンは特に指定しませんが、筆者は執筆時点(2006/10/24)での最新版の 3.2.1 で動作確認しています。http://www.eclipse.org/ より必要に応じてダウンロードして下さい。Eclipse の使用方法については Eclipse 3.2 メモ 等も参考にして下さい。

Hibernate による Hello World

Hibernate を使った開発プロセス

Hibernate を使って開発を行う場合、大きく4つの開発プロセスが存在します。

名称 説明
トップダウン 既存のPOJOをベースにマッピング・ファイルやデータベース・スキーマを(自動)生成していく方法
ボトムアップ 既存のデータベース・スキーマをベースにマッピング・ファイルやPOJOのスケルトンを(自動)生成していく方法
ミドルアウト マッピング・ファイルを生成し、そこからPOJOスケルトンやデータベース・スキーマを(自動)生成していく方法
JavaクラスとDBスキーマの結合 既存のJavaクラスと既存のデータベース・スキーマを結合する方法

Hello World の作成では、トップダウンのプロセスを採用することにします。すなわち、最初に永続化対象となるPOJOを作成します。

Eclipse プロジェクトの作成

まずは Eclipse でプロジェクトを作成しましょう。プロジェクト名は任意です。以下のように src フォルダと後に設定ファイルを格納する conf フォルダを作成し、%HIBERNATE_HOME%/hibernate3.jar にクラスパスを通します(hibernate3.jar は Eclipse の自動ビルドでビルドエラーが発生するのを防ぐために追加します)。

永続化クラスの作成(Message.java)

hibernate では基本的に一つの永続化対象クラスがデータベースの1テーブルと関連付けられます。そこで非常にシンプルな POJO をまず作成します。

[Message.java]

package jp.co.okisoft.hibernate.hello;

import java.util.Date;

public class Message {
    private Long id;
    private String text;
    private Date generatedDate;
    
    public Message() {
        this(null);
    }
    
    public Message(String text) {
        this.text = text;
        generatedDate = new Date();
    }
    
    public Date getGeneratedDate() {
        return generatedDate;
    }
    
    public void setGeneratedDate(Date generated) {
        this.generatedDate = generated;
    }
    
    public Long getId() {
        return id;
    }
    
    public void setId(Long id) {
        this.id = id;
    }
    
    public String getText() {
        return text;
    }
    
    public void setText(String text) {
        this.text = text;
    }
}

3つのフィールドと2つのコンストラクタ、及びフィールドの getter/setter を持つ Message クラスです。POJOの条件として、デフォルト・コンストラクタを用意するというものがあるため、この例のように引数有りのコンストラクタを定義した場合は、必ずデフォルト・コンストラクタも定義する必要があります。

他のクラスとの関連を持つようなクラスの例は次回以降に追加する予定です。今回はあくまでももっともシンプルな永続化クラスが対象です。

マッピング・ファイルの作成(Message.hbm.xml)

hibernate がオブジェクトとリレーショナル・データベースとのマッピングを行うためには、そのマッピングの方法を記したマッピング・ファイルが必要です。以下は Message クラスのマッピング・ファイルです。

ルート要素は <hibernate-mapping> で、その子要素に永続化対象となるクラスを定義するための <class> 要素があります。<class> 要素の子要素には永続化クラスのフィールドをどのようにマッピングするかを記しています。詳細については後述しますが、hibernate では永続化対象クラス毎にマッピング・ファイルを作成する方法が推奨されています。

[Message.hbm.xml]

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="jp.co.okisoft.hibernate.hello">
    <class name="Message" table="MESSAGE">
        <id name="id" column="MESSAGE_ID">
            <generator class="native" />
        </id>
        <property name="text" column="MESSAGE_TEXT" />
        <property name="generatedDate" column="MESSAGE_DATE"></property>
    </class>
</hibernate-mapping>

上記ファイルをファイル名 Message.hbm.xml として、Message.java と同じ場所に作成します。この配置場所も hibernate で推奨されている方法です。

Hibernate コンフィギュレーションファイルの編集

%HIBERNATE_HOME%/etc 以下に、次の二つのファイルが格納されています。

どちらも Hibernate が起動時に行うコンフィギュレーションのために必要な設定を記述するためのファイルです。設定する内容は次のようなものです。

通常は hibernate.properties か hibernate.cfg.xml のどちらか一方を利用します。これらのファイルを必要に応じて編集し、実行時のクラスパスのルートディレクトリに配置することによって、Hibernate の初期化時にこのファイルが自動的に読み込まれ、記述した設定が有効になります(Log4j の log4j.properties と log4j.xml に似ています)※1。

※1 両方のファイルがクラスパスに含まれている場合、hibernate.properties の設定は、hibernate.cfg.xml の設定で上書きされます。

プロパティ形式と XML 形式の違いは、XML 形式であればマッピングファイルの場所も設定できるということです。そのため、一般的には XML 形式の設定が推奨されます(これも Log4j と似ています)。この HelloWorld の例でも、XML 形式を使うことにします。以下に具体的なファイル内容を示します。

[hibernate.cfg.xml]

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <!-- HypersonicSQL -->
        <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
        <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
        <property name="connection.username">sa</property>
        <property name="connection.password"></property>
        <property name="connection.url">jdbc:hsqldb:hsql://localhost/sample</property>

        <!-- Miscellaneous Settings  -->
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>

        <!-- DB Schema -->
        <property name="hbm2ddl.auto">create</property>

        <!-- mapping files -->
        <mapping resource="jp/co/okisoft/hibernate/hello/Message.hbm.xml" />
    </session-factory>
</hibernate-configuration>

JDBCドライバの設定, SQLの出力設定, マッピングファイルの場所等を指定しています。<!-- DB Schema -->のところで、hbm2ddl.auto プロパティを設定し、その値を create にしています。これはマッピング・ファイルに書かれた情報を元に、データベース・テーブルを作成(もし同じテーブルがあれば削除してから作成)するためのプロパティです。つまり、この設定があればデータベースにテーブルを予め作っておく必要はありません。今回のようなサンプル・プログラムの実行に便利です。

上記ファイルを作成し、 Eclipse プロジェクトの conf フォルダへ格納します。

永続化を実行するクラスの作成

次に永続化を実行するクラスを作成します。

[Main.java]

package jp.co.okisoft.hibernate.hello;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class Main {

    public static void main(String[] args) {
        Configuration cfg = new Configuration().configure();
        SessionFactory factory = cfg.buildSessionFactory();

        Session session = factory.openSession();
        Transaction tx = session.beginTransaction();

        // Messageインスタンスの生成
        Message msg = new Message("Hello World");

        // Messageインスタンスの永続化
        session.save(msg);

        tx.commit();
        session.close();
    }
}

Configuration, SessionFactory といった重要なインタフェースが登場しています。これらのインタフェースの役割を以下に示します。

クラス名 説明
Configuration Hibernateのコンフィギュレーションと起動に使われる。SessionFactory を生成するために使われ、SessionFactory 生成後は全く使われない(つまり破棄してもかまわない)。
SessionFactory 文字通り、Sessionのファクトリ。SessionFactory はアプリケーション全体で使いまわされるべきであり、Singleton パターンを使う等、初期化の方法は工夫する必要がある。
Session DBコネクションとトランザクションを一つにまとめたようなもの。オブジェクトの永続化や取得のための操作を持つ。
Transaction 文字通りトランザクション表す。Session オブジェクトから生成される。

Ant のビルドファイル

作成したクラスのビルドと実行を行うための Ant ビルドファイルを作成します。まずは以下のように build.properties ファイルを作成し、Hibernate のインストールディレクトリの場所を設定します。

[build.properties]

HIBERNATE_HOME=C:/Myfolder/java/hibernate/hibernate-3.2

次に build.xml を作成します。単純に compile と run ターゲットを持つビルドファイルです。

[build.xml]

<project name="Hibernate3" default="run" basedir=".">

    <!--################ properties ################################-->

    <property file="build.properties" />

    <property name="dir.src" value="src" />
    <property name="dir.conf" value="conf" />
    <property name="dir.lib" value="lib" />
    <property name="dir.build" value="build" />
    <property name="dir.out.classes" value="${dir.build}/classes" />
    <property name="jar.name" value="helloworld.jar" />

    <!-- fileset definition for the lib directory -->
    <fileset id="fs.lib" dir="${HIBERNATE_HOME}">
        <include name="*.jar" />
        <include name="lib/**/*.jar" />
    </fileset>
    <path id="path.lib">
        <fileset refid="fs.lib" />
    </path>

    <!--################ Targets ################################-->

    <target name="clean">
        <delete dir="${dir.build}" />
    </target>

    <target name="init">
        <mkdir dir="${dir.build}" />
        <mkdir dir="${dir.out.classes}" />
    </target>

    <target name="compile" depends="init">
        <javac destdir="${dir.out.classes}" classpathref="path.lib" debug="on" optimize="on">
            <src path="${dir.src}" />
        </javac>
        <copy todir="${dir.out.classes}">
            <fileset dir="${dir.src}">
                <include name="**/*.xml" />
            </fileset>
            <fileset dir="${dir.conf}">
                <include name="*.properties" />
                <include name="*.xml" />
            </fileset>
        </copy>
    </target>

    <target name="run" depends="compile">
        <java classname="jp.co.okisoft.hibernate.hello.Main">
            <classpath>
                <fileset refid="fs.lib" />
                <pathelement path="${dir.out.classes}" />
            </classpath>
        </java>
    </target>

</project>

上記2つのファイルを Eclipse プロジェクト直下に配置します。ここまで来た時点で、Eclipse プロジェクトは以下のような構成になっているはずです。

実行準備

事前にデータベースを起動しておく必要があるので、再び HSQLDB を起動するために、runServer.bat を実行します。

また、%HSQLDB_HOME%/lib/hsqldb.jar を %HIBERNATE_HOME%/lib へコピーします。(当然ですが)hibernate は実行時にDBのJDBCドライバを必要とします。そのため実行時のクラスパスにJDBCドライバを含める必要がありますが、上述の build.xml で %HIBERNATE_HOME%/lib がクラスパスに含まれているので、%HIBERNATE_HOME%/lib へ格納すればJDBCドライバがクラスパスに含まれます。HSQLDBのインストールディレクトリを build.properties へ記述する方が行儀がいいですが、build.xml をなるべくシンプルにするためにこのようにしています。

実行

Eclipse 上で build.xml を実行すると、すべて成功していれば最後に以下のようなログがコンソールに出力されます。

run:
     [java] log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
     [java] log4j:WARN Please initialize the log4j system properly.
     [java] Hibernate:
     [java] insert
     [java] into
     [java] MESSAGE
     [java] (MESSAGE_ID, MESSAGE_TEXT, MESSAGE_DATE)
     [java] values
     [java] (null, ?, ?)
     [java] Hibernate:
     [java] call identity()
BUILD SUCCESSFUL

run ターゲットの実行で最初に出力されているのは Log4j のコンフィギュレーションが未設定であることを示すログです。これは後ほど設定します。

次に Hibernate が実際に発行した SQL が出力されています。Main.java では Message オブジェクトを生成して保存しているので、この保存に対応するのがこの INSERT 文です。主キーである id フィールドの値はジェネレータにより自動生成されます。

実行結果を HSQLDB のデータベース・マネージャで確認してみます。runManager.bat を実行し、SELECT を発行してみると、

と、確かに Message オブジェクトの内容が格納されています。

ところでデータベース・テーブルが実行時に自動生成されているのですが、テーブル生成のSQLがコンソールに出力されていません。これは、テーブル生成のような DDL については Log4j によって出力されるようになっているからです。

Log4j の設定と再実行

%HIBERNATE_HOME%/etc 以下の log4j.properties を Eclipse プロジェクトの conf フォルダへコピーします。デフォルトの log4j.properties では hibernate のデバッグログも出力する設定になっているため、ひとまずそれらのログを無効にします。

log4j.properties 17行目の以下の行をコメントアウトし、

log4j.logger.org.hibernate=info

再び build.xml を実行します。すると今度は以下のようなログが表示されます。

run:
     [java] 19:30:55,718  INFO SchemaExport:154 - Running hbm2ddl schema export
     [java] 19:30:55,733 DEBUG SchemaExport:170 - import file not found: /import.sql
     [java] 19:30:55,749  INFO SchemaExport:179 - exporting generated schema to database
     [java] 19:30:55,749 DEBUG SchemaExport:303 -
     [java] drop table MESSAGE if exists
     [java] 19:30:55,749 DEBUG SchemaExport:303 -
     [java] create table MESSAGE (
     [java] MESSAGE_ID bigint generated by default as identity (start with 1),
     [java] MESSAGE_TEXT varchar(255),
     [java] MESSAGE_DATE timestamp,
     [java] primary key (MESSAGE_ID)
     [java] )
     [java] 19:30:55,765  INFO SchemaExport:196 - schema export complete
     [java] Hibernate:
     [java] insert
     [java] into
     [java] MESSAGE
     [java] (MESSAGE_ID, MESSAGE_TEXT, MESSAGE_DATE)
     [java] values
     [java] (null, ?, ?)
     [java] Hibernate:
     [java] call identity()

drop table や create table といったDDLが表示されました。実際にはこのようにテーブルの破棄と生成が繰り返されているため、テーブルは毎回初期化されていることになります。これは今回のような簡単なテストには便利ですが、実際の運用ではこの機能が使われることはありません。この機能をOFFにする場合は、hibernate.cfg.xml の hbm2ddl.auto プロパティをコメントアウトして下さい。

log4j.logger.org.hibernate のログは hibernate の動作を確認するために有用ですので、コメントアウトを戻しておきましょう。また、HSQLDB の server.properties で server.silent=false のコメントアウトを外すと、HSQLDB 側でも発行されたSQLや内部動作を見ることができるため、Hibernate の動作で不明なところがあった場合の解析に便利です。

今回のまとめ

今回は Hibernate と HSQLDB を使って非常にシンプルなクラスのO/Rマッピングを行いました。今回行ったO/Rマッピングではクラス間の関連は全く無かったため、次回以降でもう少し複雑な関係を持つクラスの永続化について見ていこうと思います。

また Hibernate アプリケーションの設計方法や、Hibernate3 で導入された Annotation についても興味深いものがあります。これについても随時書いていきたいと思います。

資料室へ戻る


Copyright (c) 2006 Oki Software Co., Ltd.