hibernate
Hibernateをいじる(7)
- 2005-11-09 (水)
- テクノロジー
-


続いて、DELETEをやります。
まず、今回のサンプルのおさらいをしておきます。
![]()
Study(履修)は、Student(学生)とSubject(科目)のそれぞれを1つ持ち(Studyから見てmany-to-one)、Report(レポート)とExamination(試験)のそれぞれを複数持ちます(Studyから見てone-to-many)。
1.単純にdelete()を使う
//まず削除したいオブジェクトを取得。 Query query = session.createQuery("FROM Study AS study WHERE study.student.gakusekiNo=~ AND study.subject.code=~"); query.setString(0, "0499001"); query.setString(1, "14501"); Study study = (Study)query.uniqueResult(); //いざ削除。 session.delete(study);
簡単ですね。
今回のサンプルでは、Study.hbm.xmlで、ReportとExaminationに対してcascade=”all”が指定されています。
そのため、削除したStudyの配下にあるReportとExaminationはDBから削除されます。
もし、cascade=”none”で、DBで参照制約をかけていると、当然にエラーになります。この辺は、Hibernateだからどうこう…ではないようです。
例をもう1つ。
Study study = (Study)query.uniqueResult(); //2つ目を削除 Report report = (Report)study.getReports().get(1); //リストから消して… study.getReports().remove(report); //子をDELETE session.delete(report);
このように、親子関係のある子だけを削除する場合、先に親が持つ子のリストから、削除したい子を消しておく必要があります。
いきなり、子だけのsession.delete()をやってしまうと、エラーになります。
2.delete()を使わなくても削除できるケース
どんなケースかというと、親が持つ子のリストから、子を消して、親をupdate()するケースです。
実例を見ましょう。
Study study = (Study)query.uniqueResult(); //2つ目を削除 Report report = (Report)study.getReports().get(1); //リストから消して… study.getReports().remove(report); //親をUPDATE session.saveOrUpdate(study);
親であるstudyが持つ子のリスト(getReports()で取れるList)から、子のreportをremove()して、親のstudyをsaveOrUpdate()しています。
そうすると、リストから削除された子が消えます。
但し、要注意点が1つ。
このようなプログラムで、期待する挙動(リストから削除した子オブジェクトがDBからもDELETEされている)にするためには、Study.hbm.xmlで、cascade=”all-delete-orphan”にしなくてはなりません。
そのように設定しなければ、子オブジェクトはDBに残り、その外部KEYカラムがnullとしてUPDATEされるだけです。
Hibernateをいじる(6)
- 2005-11-09 (水)
- テクノロジー
-


久しぶりですが、Hibernate。
次は、SELECT系をまとめておきます。
1.get()で取る
単純に主KEYで取る場合に使用します。DBに該当レコードがない場合はnullが返ります。
似て非なるものにload()もあります。こちらはDBに該当レコードがないと例外になります。プロキシを使う場合だと、プロキシオブジェクトが返ります。
深いことを考えなければ、get()で良いでしょう。
2.Queryで取る
複数レコードが必要だったり、主KEY以外で検索するなら、get()やload()ではダメです。
HQLを使いましょう。
Query query = session.createQuery("FROM Student");
もうちょっと複雑な例。
Query query = session.createQuery("FROM Study AS study WHERE study.student.gakusekiNo=~ AND study.subject.code=~"); query.setString(0, "0499001"); query.setString(1, "14501"); Study study = (Study)query.uniqueResult();
このように、ちょうどPreparedStatementのようなことが出来ます。
注目する点は2つ。
1つは、Study AS studyのように、別名をつけている点です。WHERE句で使用するクラスは別名を付け、WHERE句は別名で指定します。
もう1つは、study.subject.code=~のような指定方法。WHERE句での検索条件が、FROM句で指定したテーブルではなく、何らかのJOINを行った先のテーブルのカラムである場合、SQLならJOINの指定が必要です。しかし、HQLではそれは不要で、単純に取得したいクラスをFROM句で指定し、あとはWHERE句でstudy→subject→codeとJavaオブジェクトのプロパティ名でたどっていけばOKです。クラス間(テーブル間)のマッピングが、設定ファイルで定義されているためでしょう。
Hibernateをいじる(4)
- 2005-10-24 (月)
- テクノロジー
-


さて、昨日の続きです。
今日は、5つのJavaBeans(永続クラス)に対するマッピングファイルを作り、サンプルプログラムを動かしてみます。
1.マッピングファイル
省略できるところは省略して、出来る限り分かりやすく、簡単なマッピングファイルを作ろうとすると、以下のようになります。
<~xml version="1.0"~> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="study.domain.Study"> <id name="oid"> <generator class="native"/> </id> <property name="startDate"/> <property name="endDate"/> <property name="gotCredit"/> <many-to-one name="subject" column="SUBJECT_OID"/> <many-to-one name="student" column="STUDENT_OID"/> <list name="reports"> <key column="STUDY_OID"/> <index column="no"/> <one-to-many class="study.domain.Report"/> </list> <list name="examinations"> <key column="STUDY_OID"/> <index column="no"/> <one-to-many class="study.domain.Examination"/> </list> </class> </hibernate-mapping>
今回の5つの永続クラスの中では、最も複雑なStudyのマッピングファイルなので、わりと手の込んだものになっています。(実は、今回のサンプルで使用するマッピングファイルの設定要素をすべて使っています)
(1)classエレメント
何のクラスに関する設定なのかを書きます。
hibernate-mappingエレメントでpackageアトリビュートを書かなかったので、nameはパッケージをフルに書きます。
また、クラス名とテーブル名が違う場合は、tableアトリビュートを書きます。
(2)idエレメント
テーブルでプライマリキーとなるプロパティを指定します。
nameアトリビュートで永続クラスでのプロパティ名を指定します。テーブルのカラム名が別なら、columnアトリビュートで指定できます。
idエレメントの中には、generatorエレメントが書いてあります。これは、プライマリキーの値をどのように設定するかという指定。いくつか種類がありますが、大雑把に言えば、RDBの機能を使って連番を振るか、アプリケーションで指定するからHibernateは余計なことをしなくて良いと言うか…です。
(3)propertyエレメント
プライマリキー以外のプロパティを指定します。
テーブルのカラム名が違えばcolumnアトリビュートで指定するのは、idエレメントと同じです。
但し、他の永続クラスへの関連の場合は、別のエレメントで指定します。propertyエレメントで書くと、恐るべきことに、そのオブジェクトをVARBINARYで持つことになります。(その永続クラスがSerializableな場合。そうでなければ、例外となる)
(4)many-to-oneエレメント
他の永続クラスへの関連の場合は、これです。但し、名は体を表すで、多対1の場合に限ります。1対多の場合は何らかのコレクションが必要となりますから、別のエレメントがあります。
nameアトリビュートで永続クラスでのプロパティ名を、columnアトリビュートは相手先のキー(外部キー)を自テーブルが持つ際のカラム名です。
(5)listエレメント
1対多の場合のコレクションを表現します。この例ではlistを使っていますが、他にset、map、bag等が使えます。どれを使ってもアトリビュートや、子エレメントは似通っています。
nameアトリビュートは、永続クラスでのコレクションのプロパティ名です。
key子エレメントのcolumnアトリビュートは、1対多の多側(つまり相手先)が持つ、1側(自分)の外部キーのカラム名です。注意すべきは、このカラムは自テーブルにはないということです。
双方向関連の場合、このkeyエレメントのcolumnアトリビュートの値と、相手先のmany-to-oneエレメントのcolumnアトリビュートの値を同じにすると都合が良いでしょう。
one-to-many子エレメントは、classアトリビュートで相手先のクラス名を指定します。(つまりコレクションに何クラスのオブジェクトが入っているかということです)
最後に、index子エレメントは索引を持つコレクション(listやmap)の場合に必須です。columnアトリビュートで索引カラム名を指定します。このカラムも自テーブルではなく、相手先テーブルのカラムとなります。
Hibernateをいじる(3)
- 2005-10-23 (日)
- テクノロジー
-


さっそく、Hibernateを使ってみましょう。
今回は、最新版のHibernate 3.0.5を使います。
http://www.hibernate.org/
1.使用するライブラリ
ダウンロードしたhibernate-3.0.5.zipを展開すると、様々なファイルが出てきます。
その中で、必要なのは、以下のとおりです。(libフォルダの_README.txtでrequiredと書いてあるものが必要になります。)
hibernate3.jar
asm.jar
asm-attrs.jar
antlr-2.7.5H3.jar
cglib-2.1.jar
commons-collections-2.1.1.jar
commons-logging-1.0.4.jar
dom4j-1.6.jar
ehcache-1.1.jar
jdbc2_0-stdext.jar
jta.jar
xml-apis.jar
2.JavaBeansを作る
5つのクラスのJavaBeansをPOJOで作ります。
Hibernate 2.0のマニュアルになりますが、日本語のものがありますので、それを参考にしていきます。
http://www.hibernate.org/hib_docs/reference/ja/html/
マニュアルによると、JavaBeansを作る上で、4つのルールが示されています。
- 永続フィールドに対するアクセサとミューテータを定義する
- デフォルト・コンストラクタを実装する
- 識別子プロパティを用意する(オプション)
- finalクラスにしない(オプション)
(1)オブジェクトIDを持つ
3番目のルールで、マニュアルには「すべての永続クラスで一貫した名前の識別子プロパティを定義することをおすすめします。 さらにnullでない(つまりプリミティブ型でない)型を使うことをおすすめします。」と書いてあります。
これに従い、すべてのクラスにoid(オブジェクトID)プロパティを持つことにします。
(2)関連はどうするか?
今回のモデルでは、Study~Subjectが1対1、それ以外は1対多の関連があります。
1対1の部分については、特に気にすることなくお互いの型のプロパティを持つことにします。(Study~Subjectに関しては、クラス図にあるように片方向関連です。StudyからSubjectへの関連のみとします。)
1対多については、多側は1つのオブジェクトを相手にするのみですから、問題ありません。残る1側は多を相手にするので、何らかのコレクションにしなければなりません。
使用できるコレクションは、後でマッピングファイルで指定できるものになります。Set、List、Map、Bagなどがあります。
Study→ReportとStudy→Examinationは、順番を必要としますからListを使用します。
Student→Studyは、最も単純なSetを使用します。
ちょっと、おさらいしておくと…。
Bag:順序なし。重複を許す。
Set:順序付き。重複を許さない。
List:順序付き。重複を許す。
Map:名前で管理するので順序なし。重複を許す。
次回はマッピングファイルを作ります。
Hibernateをいじる(2)
- 2005-10-23 (日)
- テクノロジー
-


さて、このシリーズの主役はHibernateです。
1.お題
今回は、このようなドメインモデルを使います。
大学通信教育の履修管理システムです。
どの科目を、いつから勉強を始めて、いつレポートを出して、単位修得試験を受験して、それぞれの評価はどうで、単位をいくつ取ったか…ということを管理します。
クラスが5つ。関連は1対1と、1対多の2種類があります。
Hibernateのサンプルとしては、継承がないのが残念ですが、これは後でいくらでも拡張できるでしょう。
例えば、Subject(科目)には、必修/選択必修/選択とか、一般教育科目/専門教育科目などの種類があります。継承のネタにはなりそうです。
今回は、初歩のサンプルですから、これで行きます。
2.Hibernateを使うためのアプローチ
Hibernateを使うためには、いくつかのモノが必要です。
まず、Hibernate自体の設定ファイルが必要です。
何らかのドメインモデルを、Hibernateを使って永続化するには、ドメインオブジェクト(もしくはテーブル)毎に、以下の種類のモノが必要となります。
- JavaBeans(Study.class)
- マッピングファイル(Study.hbm.xml)
- RDBテーブル(Studyテーブル)
ところで、この3つのうち、何からどうやって作っていくか…には、いくつかのアプローチがあります。
(1)ドメインモデルありき
つまりJavaBeansはあるので、それを元にテーブルとマッピングファイルを作っていきます。これを、Top-Downアプローチと言います。
とても、オブジェクト指向な感じですね。
このアプローチなら、XDocletでJavaBeansからマッピングファイルを作り、Hibernateの機能でマッピングファイルからテーブルを作ることが出来ます。
(2)テーブルありき
テーブルは既にあるから、それを元にJavaBeansとマッピングファイルを作ります。これを、Bottom-Upアプローチと言います。
多くの開発プロジェクトは、これになりそうな気がします。
このアプローチなら、Middlegenというツールを使って作業を自動化できます。
(3)マッピングファイルから作ろう!
逆説的な気もしますが、マッピングファイルを先に作ります。そこから、JavaBeansとテーブルを作っていきます。これを、Middle-Outアプローチと言います。
「マッピングファイル=Hibernateで出来ること」ですから、Hibernateを骨の髄までしゃぶるなら、これかもしれません。
定義がしっかりしているだけに、このアプローチをベースにした、開発ツールとか作れそうですね。
(4)マッピングファイルを作ることが面倒だ!
JavaBeansもテーブルもある。しかし、マッピングファイルだけない。だから、最後にマッピングファイルを作ろうというもの。これを、Meet-in-Middleアプローチと言います。
顧客にはテーブルで説明しないといけない、でも設計はオブジェクト指向で進んでいる…中途半端だけど、最もありそうなケースかもしれません。
今回は、既にドメインモデルがありますから、Top-Downアプローチを採ります。また、敢えてツールは使わず、手作業で進めます。

