Hibernate 入門記 Configuration

今日から本格的にHibernateの入門をはじめます.すでにバリバリHibernateを使いこなしている人がチュートリアルを書くのとは違って,大間違いをしたりうろたえたりすると思いますが,それもまた醍醐味の一つということで.(^^;
どのように学習していくか色々迷ったのですが,やはり「Hibernate Reference Documentation」を頼りにしていくことにします.とはいえ,やはり英語より日本語に決まっているので,実際にはozaccさんによる翻訳を読んでいることは言うまでもありません.


というわけで,まずは「Chapter 1. Quickstart with Tomcat」からスタートです.
...
なんでリファレンスの最初がこのテーマ? リファレンスなのに? チュートリアルとして切り出しておけばいいのに.
ここではTomcatに興味ないし,入門寸前で軽く遊んでおいたので,ここはスキップしてしまいましょう.いやその,決して翻訳の方にこの章がないから飛ばすわけではありません,あうあう.


ということで,次は「Chapter 2. Architecture」です.ま,この辺りは字よりも図を見ておけばいいかなぁ,って感じですね.
むむぅ,翻訳(バージョン2.0.x)にあった「1.2. 永続オブジェクトの同一性 (Identity)」が2.1.xからはなくなってるぅ! どこかに移動したんでしょうか?
後はJMXJCAに対応しているよってことが紹介されていますが,それしか書いてないのでどうでもよさげ.これで2章も終わり♪


そして次は「Chapter 3. SessionFactory Configuration」へと進みます.ここからが本格的な解説の始まりっぽい.
まずは「3.1. Programmatic Configuration」.
Hibernateは,プログラマティックに設定することも,XMLファイルで設定することもできます.そのプログラマティックな設定についての解説です.
Hibernateの諸々の構成は

  • Configuration

というクラスを用いて設定します.これは,マッピング情報(XMLファイル)も保持します.そのために用意されているメソッドが,

    • Configuration addFile(String)
    • Configuration addClass(Class)

など.前者はマッピングファイルのパスを引数で渡します.後者は,クラスの完全限定名を元に拡張子 .cfg.xml をつけたリソースをクラスローダーを使って読み込んでくれるというものです.
他にもディレクトリやJARファイルを渡すと再帰的にマッピングファイルを探してくれるものなど,豊富に用意されています.
面白いことに,これらのメソッドの戻り値は Configuration 自身です.return this; してるんですね.おかげで

Configuration cfg = new Configuration()
    .addClass(org.hibernate.auction.Item.class)
    .addClass(org.hibernate.auction.Bid.class);

みたいに書くことができる,と.Smalltalkではよく見かける書き方ですが,Javaで見るとちょっとドッキリします(StringBuffer ではよく使いますね).


ちょっと飛ばして先に「3.8. XML Configuration File」へ行っちゃいましょう.
構成をXMLで設定するには,Configuraion

    • Configuration configure()
    • Configuration configure(String)

などを使います.前者のメソッドの場合は,クラスローダーを使って hibernate.cfg.xml という名前のリソースを探してくれます.後者だとクラスローダーから指定した名前のリソースを探してくれます.
他にもDOMのDocumentを渡すものなどが用意されています.
XMLの書き方については説明されていません.(^^; 例を見れば十分だろ? ってことでしょう.はい,十分だと思います.基本的にプロパティとマッピングファイルの設定だけですからね.


で,ちょっとあちこちに散らばっているConfigurationに関する情報をここで.
Configurationはプロパティを持ちます.
まずはクラスローダーから hibernate.properties というリソースを取得できれば,それを読み込んで設定してくれるみたい.
そしてシステムプロパティの値で上書きされます.
Configuration#configure()等で使われたXML<property>要素が含まれていれば,そのプロパティが設定されます.
このように設定されたプロパティを変更するには,Configuration

    • Configuration setProperties(Properties)
    • Configuration setProperty(String, String)

などで変更することができます.前者の場合は,以前のプロパティリストを丸ごと置き換えちゃうみたい(追加ではないっぽい).
Configurationについて,現時点ではこれくらいですかね.他にもいろいろメソッドがあるのですが,もっとHibernateのことを理解しないとよく分からないものもあるので.無念だ.


次は「3.2. Obtaining a SessionFactory」です.
Configurationの準備ができたら

  • SessionFactory

を取得することができます.そのために使うのがConfiguration

    • buildSessionFactory()

です.SessionFactoryはシングルトンではなく,buildSessionFactory()は呼び出されるたびに新しいSessionFactoryを作って返すとのこと.SessionFactoryを共有するの仕掛けはアプリケーションで用意しなきゃいけないのですね.だから色々な解説でgetSessionFactory()とかやっていたわけかぁ.で,そのスマートな解決方法がSpringやS2などのDIConというわけですね.ふむふむ.


どんどん進んで次は「3.3. User provided JDBC connection」と「3.4. Hibernate provided JDBC connection」です.
SessionFactoryを取得すると,そこから

  • Session

を取得できます.SessionJDBCコネクションを利用して永続オブジェクトとDBとのやり取りを行ってくれるものです.ということでJDBCコネクションが必要になるのですが,

  • Hibernateの外から与えるJDBCコネクションを使う.
  • HibernateDriverManagerから取得したJDBCコネクションを使う.
  • HibernateがJNDI経由のDataSourceから取得したJDBCコネクションを使う.

といった方法を使うことができます.
最初の場合はSessionFactory

    • Session openSession(Connection)

を使います.後の二つはやはりSessionFactory

    • Session openSession()

を使います.必要な情報はConfigurationに設定したプロパティから取得するとのこと.
実際に設定するプロパティの名前や値などは必要に応じてドキュメントを参照するということで.(^^; だって,DIConを使うとコネクションは外から与えるわけで,あうあう.手抜きじゃないもーん,面倒なだけ〜.一緒か.心より恥じる.
おっと,翻訳(2.0.x)だとなぜかここでSession#beginTransaction()のことがオプションだとかって書いてあるのですが,2.1.xのドキュメントからは削られていますね.うん,ここでは書かない方がいいと思う.
あと,SessionFactoryはコネクションプールの機能も持っているのですね.DIConとか使う場合は別途コネクションプールを使って,そこから得たコネクションをSessionFactory#openSession(Connection)に渡すことになると思いますが,ちょっとHibernateだけ使う場合でも手軽にコネクションプールがつかえるのはいいかも.でもそれってどんな場合?


そして「3.5. Optional configuration properties」へ.
すげー,プロパティがたくさんある! いろいろ設定できるらしい.とりあえず重要そうなのは,

hibernate.dialect
RDBMSの実装の違いを吸収するために使用するDialect実装クラスを指定します.主要なRDBMSに対応した実装クラスが標準で提供されているので,たいていはそれを使うだけでよさげ.でも,国内ベンダのRDBMSは自前でDialectを用意しなきゃいけないみたい.id:ukki4903さん頑張れ!
hibernate.default_schema
RDBMSのテーブル等を参照する際に修飾するスキーマ名を指定できます.
hibernate.cglib.use_reflection_optimizer
えぇっ? ここでもCGLIB!? 何に使ってるんだろう? ドキドキ!
hibernate.transaction.factory_class
トランザクションを制御するためのクラスを指定するらしい.JDBCトランザクションを使う場合とJTAを使う場合の実装クラスが用意されているらしい.DIConを使う場合はHibernateの外でトランザクションを制御することになると思うので,それほど気にする必要はないように思います.
hibernate.show_sql
true に設定すると,実行するSQLをロギングしてくれます.

他にもたくさんあるので困ったときは見ることにしよう.


そろそろ疲れた.でも残り少しみたいなので頑張るべし.
3.6. Logging」によると,ロギングにはLog4JまたはJDK1.4のロギングAPIを使うとのこと.らじゃ.


で,「3.7. Implementing a NamingStrategy」です.
何でも,NamingStrategy という Interface が用意されていて,その実装クラスを Configuration#setNamingStrategy(NamingStrategy) で設定すると,マッピングファイルに記述された「論理的な」テーブル名やカラム名から,「物理的な」名前に自動的に変換してくれるのだそうです.ふーむ.
そういえば,入門寸前にマッピングファイルを作った際,永続オブジェクトのプロパティに対応するDBのカラム名の指定は任意となっていました(プロパティ名と同じカラム名になる).なるほど,例えば firstName というプロパティに対応するカラム名FIRST_NAME なんていう場合にいちいちマッピングファイルに書かなくても,これ一発で済むかもしれないということですか.それはよいかも.
一応,ImprovedNamingStrategy という実装クラスが用意されていて,これがまさに '_' を埋め込んでくれる代物らしいです.


ふぅ〜っ,どうにか3章の最後まで到達.ということで実験コーナー!
ここはやっぱり NamingStrategy でしょう.それしか楽しめるものが見当たりません.
ということで,入門寸前で使ったCostomerクラスとマッピングファイルはそのままに,テーブルのカラム名firstnamefirst_name に,lastnamelast_name に変えてみましょう.
ぐはぁっ,HSQLDBではカラム名を変更できないの!? しょうがないので新しいカラムを追加して値をコピーして古いカラムを削除っと.
そして入門寸前の Main をそのまま実行!!

 Hibernate: select customer0_.id as id, customer0_.firstName as firstName, customer0_.lastName as lastName, customer0_.street as street, customer0_.city as city from customer customer0_
 - SQL Error: -28, SQLState: S0022
 - Column not found: FIRSTNAME in statement [select customer0_.id as id, customer0_.firstName as firstName, customer0_.lastName as lastName, customer0_.street as street, customer0_.city as city from customer customer0_]
 - SQL Error: -28, SQLState: S0022
 - Column not found: FIRSTNAME in statement [select customer0_.id as id, customer0_.firstName as firstName, customer0_.lastName as lastName, customer0_.street as street, customer0_.city as city from customer customer0_]
 - Could not execute query
 java.sql.SQLException: Column not found: FIRSTNAME in statement [select customer0_.id as id, customer0_.firstName as firstName, customer0_.lastName as lastName, customer0_.street as street, customer0_.city as city from customer customer0_]
・
・
・

うん,きっちり FIRSTNAME というカラムが見つからないと吹っ飛んでくれました.
ということで,NamingStrategy を組み込んでみましょう.
Main クラスで Configuration を生成した直後に

            config.setNamingStrategy(ImprovedNamingStrategy.INSTANCE);

を追加します(import文も).
そして再び実行!!

 Hibernate: select customer0_.id as id, customer0_.first_name as first_name, customer0_.last_name as last_name, customer0_.street as street, customer0_.city as city from customer customer0_
 0 : Laura Steel, 429 Seventh Av. Dallas
 1 : Susanne King, 366 - 20th Ave. Olten
 2 : Anne Miller, 20 Upland Pl. Lyon
・
・
・

バッチリ!


ということで,3章まで一気に終わりです.やったね! っていうか,あまり面白くなかった気もするのですが(苦笑),しょうがないってことで.
それよりもですね,今回が記念すべき「Hibernate 入門記」の第一回であるような気がするのですが,お試しコーナーのソースやXMLは「入門寸前」に頼っているってどういうこと? 入門寸前って完璧に入門の一部じゃん!
ま,何でも予定通りには進まないということです.心より恥じる.