Hibernateを使い始める

Javaアプリケーションでのオブジェクト/リレーショナルマッピング(ORM)の必要性を理解するのは良いことですが、おそらくHibernateが実際に動作するのを見たいと思っているでしょう。その力の一部を示す簡単な例を示すことから始めます。

ご存知かもしれませんが、プログラミングの本は「HelloWorld」の例から始めるのが伝統的です。この章では、比較的単純な「Hello World」プログラムでHibernateを紹介することにより、その伝統に従います。ただし、メッセージをコンソールウィンドウに出力するだけでは、Hibernateを実際に示すには不十分です。代わりに、プログラムは新しく作成されたオブジェクトをデータベースに格納し、それらを更新し、クエリを実行してデータベースからオブジェクトを取得します。

標準的な「HelloWorld」の例に加えて、コアのHibernate APIを紹介し、基本的な構成の詳細を示します。

Hibernateを使用した「HelloWorld」

休止状態のアプリケーションは、データベーステーブルに「マップ」される永続クラスを定義します。「HelloWorld」の例は、1つのクラスと1つのマッピングファイルで構成されています。単純な永続クラスがどのように見えるか、マッピングがどのように指定されるか、そしてHibernateを使用して永続クラスのインスタンスで実行できるいくつかのことを見てみましょう。

サンプルアプリケーションの目的は、メッセージをデータベースに保存し、表示用に取得することです。アプリケーションには、Messageこれらの印刷可能なメッセージを表す単純な永続クラスがあります。私たちのMessageクラスをリスト1に示します。

リスト1.Message.java:単純な永続クラス

パッケージこんにちは;パブリッククラスメッセージ{プライベートロングID;プライベート文字列テキスト。プライベートメッセージnextMessage; private Message(){} public Message(String text){this.text = text; } public Long getId(){return id; } private void setId(Long id){this.id = id; } public String getText(){テキストを返す; } public void setText(String text){this.text = text; } public Message getNextMessage(){return nextMessage; } public void setNextMessage(Message nextMessage){this.nextMessage = nextMessage; }}

このMessageクラスには、識別子属性、メッセージのテキスト、および別のへの参照という3つの属性がありますMessage。識別子属性を使用すると、アプリケーションは永続オブジェクトのデータベースID(主キー値)にアクセスできます。の2つのインスタンスがMessage同じ識別子値を持っている場合、それらはデータベース内の同じ行を表します。Long識別子属性のタイプを選択しましたが、これは必須ではありません。後で説明するように、Hibernateは識別子タイプに対して事実上すべてを許可します。

Messageクラスのすべての属性にJavaBeanスタイルのプロパティアクセサメソッドがあることに気付いたかもしれません。このクラスには、パラメーターのないコンストラクターもあります。この例で使用する永続クラスは、ほとんどの場合、次のようになります。

MessageクラスのインスタンスはHibernateによって管理(永続化)される場合がありますが、そうである必要はありません。MessageオブジェクトはHibernate固有のクラスまたはインターフェースを実装していないため、他のJavaクラスと同じように使用できます。

メッセージmessage = new Message( "Hello World"); System.out.println(message.getText());

このコードフラグメントは、「HelloWorld」アプリケーションに期待されることを正確に実行します"Hello World"。コンソールに出力します。ここでかわいくしようとしているように見えるかもしれません。実際、HibernateをEJB(Enterprise JavaBean)エンティティBeanなどの他の永続化ソリューションと区別する重要な機能を示しています。永続クラスは、任意の実行コンテキストで使用できます。特別なコンテナは必要ありません。もちろん、Hibernate自体を見るためにここに来たのでMessage、データベースに新しいものを保存しましょう。

セッションsession = getSessionFactory()。openSession(); トランザクションtx = session.beginTransaction(); メッセージmessage = new Message( "Hello World"); session.save(メッセージ); tx.commit(); session.close();

このコードは、HibernateSessionTransactionインターフェイスを呼び出します。(getSessionFactory()すぐにその呼び出しに到達します。)これにより、次のSQLのようなものが実行されます。

MESSAGES(MESSAGE_ID、MESSAGE_TEXT、NEXT_MESSAGE_ID)に値(1、 'Hello World'、null)を挿入します 

しばらくお待ちください—MESSAGE_ID列が奇妙な値に初期化されています。どこにもidプロパティを設定していなかったmessageので、そうなるnullと思いますよね?実際、このidプロパティは特別です。これは識別子プロパティであり、生成された一意の値を保持します。(値がどのように生成されるかについては後で説明します。)値はMessagesave()が呼び出されたときにHibernateによってインスタンスに割り当てられます。

この例では、MESSAGESテーブルがすでに存在していると想定しています。もちろん、「HelloWorld」プログラムでメッセージをコンソールに出力する必要があります。データベースにメッセージが入ったので、これを示す準備ができました。次の例では、データベースからすべてのメッセージをアルファベット順に取得して出力します。

セッションnewSession = getSessionFactory()。openSession(); トランザクションnewTransaction = newSession.beginTransaction(); メッセージの一覧表示= newSession.find( "from Message as m order by m.text asc"); System.out.println(messages.size()+ "メッセージが見つかりました:"); for(Iterator iter = messages.iterator(); iter.hasNext();){メッセージメッセージ=(メッセージ)iter.next(); System.out.println(message.getText()); } newTransaction.commit(); newSession.close();

リテラル文字列"from Message as m order by m.text asc"はHibernateクエリであり、Hibernate独自のオブジェクト指向Hibernateクエリ言語(HQL)で表現されます。このクエリは、find()が呼び出されると、内部で次のSQLに変換されます。

MESSAGESからm.MESSAGE_ID、m.MESSAGE_TEXT、m.NEXT_MESSAGE_IDを選択します。m.MESSAGE_TEXTascで並べ替えます。 

コードフラグメントは次のように出力します。

1つのメッセージが見つかりました:Hello World 

これまでHibernateのようなORMツールを使用したことがない場合は、コードまたはメタデータのどこかにSQLステートメントが表示されることを期待していた可能性があります。彼らはそこにいません。すべてのSQLは実行時に生成されます(実際には、すべての再利用可能なSQLステートメントの起動時に)。

この魔法を発生させるには、HibernateはMessageクラスを永続化する方法に関する詳細情報を必要とします。この情報は通常、XMLマッピングドキュメントで提供されます。マッピングドキュメントは、とりわけ、MessageクラスのプロパティがMESSAGESテーブルの列にどのようにマップされるかを定義します。リスト2のマッピングドキュメントを見てみましょう。

リスト2.単純なHibernateXMLマッピング


  

マッピングドキュメントは、MessageクラスがMESSAGESテーブルに永続化されること、識別子プロパティがという名前の列にマップされるMESSAGE_IDこと、テキストプロパティがという名前の列にマップされるMESSAGE_TEXTこと、および名前nextMessageが付けられたプロパティが多対1の関連付けであることをHibernateに通知します。NEXT_MESSAGE_ID。という名前の列にマップされる多重度。(今のところ、他の詳細については心配しないでください。)

ご覧のとおり、XMLドキュメントを理解するのは難しくありません。手で簡単に書いてメンテナンスできます。どちらの方法を選択しても、Hibernateには、Messageクラスのインスタンスの挿入、更新、削除、および取得に必要なすべてのSQLステートメントを完全に生成するのに十分な情報があります。これらのSQLステートメントを手動で記述する必要はなくなりました。

注意
多くのJava開発者は、J2EE開発に伴う「メタデータの地獄」に不満を持っています。XMLメタデータからプレーンJavaコードに戻ることを提案する人もいます。いくつかの問題についてこの提案を称賛しますが、ORMはテキストベースのメタデータが本当に必要な場合を表しています。Hibernateには、入力を最小限に抑える賢明なデフォルトと、エディターでのオートコンプリートまたは検証に使用できる成熟したドキュメントタイプ定義があります。さまざまなツールを使用してメタデータを自動的に生成することもできます。

次に、最初のメッセージを変更し、リスト3に示すように、最初のメッセージに関連付けられた新しいメッセージを作成します。

リスト3.メッセージの更新

セッションsession = getSessionFactory()。openSession(); トランザクションtx = session.beginTransaction(); // 1は、最初のメッセージの生成されたIDです。Messagemessage=(Message)session.load(Message.class、new Long(1)); message.setText( "Greetings Earthling"); メッセージnextMessage = new Message( "Take me to your Leader(please)"); message.setNextMessage(nextMessage); tx.commit(); session.close();

このコードは、同じトランザクション内で3つのSQLステートメントを呼び出します。

MESSAGES mからm.MESSAGE_ID、m.MESSAGE_TEXT、m.NEXT_MESSAGE_IDを選択します。ここでm.MESSAGE_ID = 1はMESSAGES(MESSAGE_ID、MESSAGE_TEXT、NEXT_MESSAGE_ID)の値に挿入しますset MESSAGE_TEXT = 'Greetings Earthling'、NEXT_MESSAGE_ID = 2ここで、MESSAGE_ID = 1 

Hibernateが最初のメッセージのプロパティtextnextMessageプロパティの変更を検出し、データベースを自動的に更新したことに注目してください。自動ダーティチェックと呼ばれるHibernate機能を利用しました。この機能により、トランザクション内のオブジェクトの状態を変更するときに、Hibernateにデータベースの更新を明示的に要求する手間が省けます。同様に、最初のメッセージから参照が作成されたときに、新しいメッセージが永続化されたことがわかります。この機能はカスケード保存と呼ばれます。これを呼び出すことで、新しいオブジェクトを明示的に永続化する手間を省くことができます。save()、すでに永続的なインスタンスから到達可能である限り。また、SQLステートメントの順序は、プロパティ値を設定する順序と同じではないことに注意してください。Hibernateは高度なアルゴリズムを使用して、データベースの外部キー制約違反を回避しながら、ユーザーが十分に予測できる効率的な順序を決定します。この機能は、トランザクション後書きと呼ばます。

「HelloWorld」を再度実行すると、次のように出力されます。

2つのメッセージが見つかりました:Greetings Earthling私をあなたのリーダーに連れて行ってください(お願いします) 

これは、「HelloWorld」アプリケーションを使用する限りです。ついにコードが完成したので、一歩下がってHibernateのメインAPIの概要を示します。

アーキテクチャを理解する

The programming interfaces are the first thing you have to learn about Hibernate in order to use it in the persistence layer of your application. A major objective of API design is to keep the interfaces between software components as narrow as possible. In practice, however, ORM APIs aren't especially small. Don't worry, though; you don't have to understand all the Hibernate interfaces at once. The figure below illustrates the roles of the most important Hibernate interfaces in the business and persistence layers.

We show the business layer above the persistence layer, since the business layer acts as a client of the persistence layer in a traditionally layered application. Note that some simple applications might not cleanly separate business logic from persistence logic; that's okay—it merely simplifies the diagram.

The Hibernate interfaces shown in the figure above may be approximately classified as follows:

  • Interfaces called by applications to perform basic CRUD (create/read/update/delete) and querying operations. These interfaces are the main point of dependency of application business/control logic on Hibernate. They include Session, Transaction, and Query.
  • Interfaces called by application infrastructure code to configure Hibernate, most importantly, the Configuration class.
  • Callback interfaces that allow the application to react to events occurring inside Hibernate, such as Interceptor, Lifecycle, and Validatable.
  • Interfaces that allow extension of Hibernate's powerful mapping functionality, such as UserType, CompositeUserType, and IdentifierGenerator. These interfaces are implemented by application infrastructure code (if necessary).

Hibernate makes use of existing Java APIs, including JDBC (Java Database Connectivity), Java Transaction API (JTA), and Java Naming and Directory Interface (JNDI). JDBC provides a rudimentary level of abstraction of functionality common to relational databases, allowing almost any database with a JDBC driver to be supported by Hibernate. JNDI and JTA allow Hibernate to be integrated with J2EE application servers.

このセクションでは、Hibernate APIメソッドの詳細なセマンティクスについては説明せず、各プライマリインターフェイスの役割についてのみ説明します。これらのインターフェースのほとんどは、パッケージに含まれていますnet.sf.hibernate。各インターフェースを順番に簡単に見ていきましょう。

コアインターフェイス

5つのコアインターフェイスは、ほぼすべてのHibernateアプリケーションで使用されます。これらのインターフェースを使用して、永続オブジェクトを格納および取得し、トランザクションを制御できます。

セッションインターフェース