Hibernate.org Community Documentation

- 前のページ
- 次のページ

## 第1章 Tutorial

Intended for new users, this chapter provides an step-by-step introduction to Hibernate, starting with a simple application using an in-memory database. The tutorial is based on an earlier tutorial developed by Michael Gloegl. All code is contained in the `tutorials/web` directory of the project source.

## 重要項目

This tutorial expects the user have knowledge of both Java and SQL. If you have a limited knowledge of JAVA or SQL, it is advised that you start with a good introduction to that technology prior to attempting to learn Hibernate.

## 注意

The distribution contains another example application under the `tutorial/eg` project source directory.

## 1.1. パート1 - 初めての Hibernate アプリケーション

仮に小さなデータベースアプリケーションが必要だとしましょう。そのアプリケーションには出席したいイベントと、そのイベントのホストについての情報を格納するものとします。

## 注意

Although you can use whatever database you feel comfortable using, we will use HSQLDB (an in-memory, Java database) to avoid describing installation/setup of any particular database servers.

### 1.1.1. Setup

The first thing we need to do is to set up the development environment. We will be using the "standard layout" advocated by alot of build tools such as Maven. Maven, in particular, has a good resource describing this layout. As this tutorial is to be a web application, we will be creating and making use of `src/main/java`, `src/main/resources` and `src/main/webapp` directories.

We will be using Maven in this tutorial, taking advantage of its transitive dependency management capabilities as well as the ability of many IDEs to automatically set up a project for us based on the maven descriptor.

    <project xmlns="http://maven.apache.org/POM/4.0.0"

             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

        <modelVersion>4.0.0</modelVersion>

        <groupId>org.hibernate.tutorials</groupId>

        <artifactId>hibernate-tutorial</artifactId>

        <version>1.0.0-SNAPSHOT</version>

        <name>First Hibernate Tutorial</name>

        <build>

             <!-- we dont want the version to be part of the generated war file name -->

             <finalName>${artifactId}</finalName>

        </build>

        <dependencies>

            <dependency>

                <groupId>org.hibernate</groupId>

                <artifactId>hibernate-core</artifactId>

            </dependency>

            <!-- Because this is a web app, we also have a dependency on the servlet api. -->

            <dependency>

                <groupId>javax.servlet</groupId>

                <artifactId>servlet-api</artifactId>

            </dependency>

            <!-- Hibernate uses slf4j for logging, for our purposes here use the simple backend -->

            <dependency>

                <groupId>org.slf4j</groupId>

                <artifactId>slf4j-simple</artifactId>

            </dependency>

            <!-- Hibernate gives you a choice of bytecode providers between cglib and javassist -->

            <dependency>

                <groupId>javassist</groupId>

                <artifactId>javassist</artifactId>

            </dependency>

        </dependencies>

    </project>

## ティップ

It is not a requirement to use Maven. If you wish to use something else to build this tutorial (such as Ant), the layout will remain the same. The only change is that you will need to manually account for all the needed dependencies. If you use something like Ivy providing transitive dependency management you would still use the dependencies mentioned below. Otherwise, you'd need to grab all dependencies, both explicit and transitive, and add them to the project's classpath. If working from the Hibernate distribution bundle, this would mean `hibernate3.jar`, all artifacts in the `lib/required` directory and all files from either the `lib/bytecode/cglib` or `lib/bytecode/javassist` directory; additionally you will need both the servlet-api jar and one of the slf4j logging backends.

Save this file as `pom.xml` in the project root directory.

### 1.1.2. 最初のクラス

次にデータベースに格納するイベントを表すクラスを作成します。

    package org.hibernate.tutorial.domain;

    import java.util.Date;

    public class Event {

        private Long id;

        private String title;

        private Date date;

        public Event() {}

        public Long getId() {

            return id;

        }

        private void setId(Long id) {

            this.id = id;

        }

        public Date getDate() {

            return date;

        }

        public void setDate(Date date) {

            this.date = date;

        }

        public String getTitle() {

            return title;

        }

        public void setTitle(String title) {

            this.title = title;

        }

    }

This class uses standard JavaBean naming conventions for property getter and setter methods, as well as private visibility for the fields. Although this is the recommended design, it is not required. Hibernate can also access fields directly, the benefit of accessor methods is robustness for refactoring.

`id` プロパティは、ある特定のイベントに対するユニークな識別子の値を保持します。 Hibernate の完全な機能を使いたければ、すべての永続エンティティクラス （それほど重要ではない依存クラスというものもあります） にこのような識別子プロパティが必要になります。事実上ほとんどのアプリケーション （ 特に web アプリケーション） では、識別子でオブジェクトを区別する必要があるため、これは制限というよりも特徴であると考えるべきです。しかし通常オブジェクトの ID を操作するようなことはしません。そのためセッターメソッドは private にするべきです。 Hibernate だけがオブジェクトがセーブされたときに識別子へ値を代入します。 Hibernate が（public, private, protected）フィールドに直接アクセスできるのと同様に、 public, private, protected のアクセサメソッドにアクセスできるということがわかるでしょう。選択はあなたに任されているので、あなたのアプリケーションの設計に合わせることができます。

引数のないコンストラクタはすべての永続クラスに必須です。これは Hibernate が Java のリフレクションを使って、オブジェクトを作成しなければならないためです。コンストラクタを private にすることは可能ですが、実行時のプロキシ生成と、バイトコード操作なしの効率的なデータの抽出には、 package 可視性が必要です。

Save this file to the `src/main/java/org/hibernate/tutorial/domain` directory.

### 1.1.3. マッピングファイル

Hibernate は、どのように永続クラスのオブジェクトをロードし格納すればよいかを知る必要があります。ここで Hibernate マッピングファイルが登場します。マッピングファイルは、データベース内のどのテーブルにアクセスしなければならないか、そのテーブルのどのカラムを使うべきかを、 Hibernate に教えます。

マッピングファイルの基本的な構造はこのようになります：

    <?xml version="1.0"?>

    <!DOCTYPE hibernate-mapping PUBLIC

            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

    <hibernate-mapping package="org.hibernate.tutorial.domain">

    [...]

    </hibernate-mapping

    >

Hibernate DTD が非常に洗練されていることに注目してください。この DTD は、エディタや IDE での XML マッピング要素と属性のオートコンプリーション機能に利用できます。また DTD ファイルをテキストエディタで開けてみてください。というのも、すべての要素と属性を概観し、コメントやデフォルトの値を見るには一番簡単な方法だからです。 Hibernate は、 web から DTD ファイルをロードせずに、まずアプリケーションのクラスパスからこれを探し出そうとすることに注意してください。 DTD ファイルは Hibernate ディストリビューションの `src/` ディレクトリと同様、`hibernate3.jar` にも含まれています。

## 重要項目

以降の例ではコードを短くするために DTD 宣言を省略します。当然ですがこれはオプションではありません。

2つの `hibernate-mapping` タグの間に `class` 要素を含めてください。すべての永続エンティティクラス（念を押しますが、ファーストクラスのエンティティではない依存クラスというものが後ほど登場します）は SQL データベース内のテーブルへのこのようなマッピングを必要とします。

    <hibernate-mapping package="org.hibernate.tutorial.domain">

        <class name="Event" table="EVENTS">

        </class>

    </hibernate-mapping>

これまで私たちは、 `Event` クラスのオブジェクトを `EVENTS` テーブルに対して、どのように永続化したりロードしたりするのかを Hibernate に教えてきました。そして個々のインスタンスはテーブルの行として表現されます。それでは引き続きテーブルの主キーに対するユニークな識別子プロパティをマッピングしていきます。さらに、この識別子の扱いに気を使いたくなかったのと同様に、代理の主キーカラムに対する Hibernate の識別子生成戦略を設定します。

    <hibernate-mapping package="org.hibernate.tutorial.domain">

        <class name="Event" table="EVENTS">

            <id name="id" column="EVENT_ID">

                <generator class="native"/>

            </id>

        </class>

    </hibernate-mapping>

The `id` element is the declaration of the identifier property. The `name="id"` mapping attribute declares the name of the JavaBean property and tells Hibernate to use the `getId()` and `setId()` methods to access the property. The column attribute tells Hibernate which column of the `EVENTS` table holds the primary key value.

The nested `generator` element specifies the identifier generation strategy (aka how are identifier values generated?). In this case we choose `native`, which offers a level of portability depending on the configured database dialect. Hibernate supports database generated, globally unique, as well as application assigned, identifiers. Identifier value generation is also one of Hibernate's many extension points and you can plugin in your own strategy.

## ティップ

`native` is no longer consider the best strategy in terms of portability. for further discussion, see 「Identifier generation」

最後にクラスの永続プロパティの宣言をマッピングファイルに含めます。デフォルトでは、クラスのプロパティは永続と見なされません：

    <hibernate-mapping package="org.hibernate.tutorial.domain">

        <class name="Event" table="EVENTS">

            <id name="id" column="EVENT_ID">

                <generator class="native"/>

            </id>

            <property name="date" type="timestamp" column="EVENT_DATE"/>

            <property name="title"/>

        </class>

    </hibernate-mapping>

`id` 要素の場合と同様に、 `property` 要素の `name` 属性で、どのゲッターとセッターメソッドを使うべきかを Hibernate に教えます。この例では、 Hibernate は `getDate()/setDate()` と `getTitle()/setTitle()` を探します。

## 注意

なぜ `date` プロパティのマッピングには `column` 属性があり、 `title` プロパティにはないのでしょうか？ `column` 属性がなければ、 Hibernate はデフォルトでプロパティ名をカラム名として使います。これは `title` では上手くいきます。しかし `date` は、ほとんどのデータベースで予約語なので、違う名前でマッピングした方がよいのです。

次に興味深いのは `title` マッピングが `type` 属性をも欠いている点です。マッピングファイルで宣言して使う type は、おわかりかもしれませんが Java のデータ型ではありません。 SQL データベースの型でもありません。これは Hibernateマッピング型 と呼ばれる、 Java から SQL データの型へまたは SQL から Java データ型へ翻訳するコンバータです。繰り返しになりますが、 Hibernate は `type` 属性がマッピングファイル内になければ、正しいコンバージョンとマッピング型を自分で解決しようとします。 （Javaクラスのリフレクションを使った）この自動検知は、場合によってはあなたが期待または必要とするデフォルト値にならないかもしれません。 `date` プロパティの場合がそうでした。 Hibernate はこの（ `java.util.Date` の）プロパティを SQL の `date` , `timestamp` , `time` のうち、どのカラムにマッピングするべきなのかわかりません。 `timestamp` コンバータでプロパティをマッピングすることにより、完全な日時を保存します。

## ティップ

Hibernate makes this mapping type determination using reflection when the mapping files are processed. This can take time and resources, so if startup performance is important you should consider explicitly defining the type to use.

Save this mapping file as `src/main/resources/org/hibernate/tutorial/domain/Event.hbm.xml`.

### 1.1.4. Hibernate の設定

At this point, you should have the persistent class and its mapping file in place. It is now time to configure Hibernate. First let's set up HSQLDB to run in "server mode"

## 注意

We do this do that the data remains between runs.

We will utilize the Maven exec plugin to launch the HSQLDB server by running: `mvn exec:java -Dexec.mainClass="org.hsqldb.Server" -Dexec.args="-database.0 file:target/data/tutorial"` You will see it start up and bind to a TCP/IP socket; this is where our application will connect later. If you want to start with a fresh database during this tutorial, shutdown HSQLDB, delete all files in the `target/data` directory, and start HSQLDB again.

Hibernate will be connecting to the database on behalf of your application, so it needs to know how to obtain connections. For this tutorial we will be using a standalone connection pool (as opposed to a `javax.sql.DataSource`). Hibernate comes with support for two third-party open source JDBC connection pools: c3p0 and proxool. However, we will be using the Hibernate built-in connection pool for this tutorial.

## 注意

The built-in Hibernate connection pool is in no way intended for production use. It lacks several features found on any decent connection pool.

Hibernate の設定では、単純な `hibernate.properties` ファイル、それより少し洗練されている `hibernate.cfg.xml` ファイル、または完全にプログラム上でセットアップする方法が利用できます。ほとんどのユーザーが好むのは XML 設定ファイルです：

    <?xml version='1.0' encoding='utf-8'?>

    <!DOCTYPE hibernate-configuration PUBLIC

            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>

        <session-factory>

            <!-- Database connection settings -->

            <property name="connection.driver_class"

    >org.hsqldb.jdbcDriver</property>

            <property name="connection.url"

    >jdbc:hsqldb:hsql://localhost</property>

            <property name="connection.username"

    >sa</property>

            <property name="connection.password"

    ></property>

            <!-- JDBC connection pool (use the built-in) -->

            <property name="connection.pool_size"

    >1</property>

            <!-- SQL dialect -->

            <property name="dialect"

    >org.hibernate.dialect.HSQLDialect</property>

            <!-- Enable Hibernate's automatic session context management -->

            <property name="current_session_context_class"

    >thread</property>

            <!-- Disable the second-level cache  -->

            <property name="cache.provider_class"

    >org.hibernate.cache.NoCacheProvider</property>

            <!-- Echo all executed SQL to stdout -->

            <property name="show_sql"

    >true</property>

            <!-- Drop and re-create the database schema on startup -->

            <property name="hbm2ddl.auto"

    >update</property>

            <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>

        </session-factory>

    </hibernate-configuration

    >

## 注意

この XML の設定が異なる DTD を使うことに注意してください。

特定のデータベースを受け持つグローバルファクトリである Hibernate の `SessionFactory` を設定します。もし複数のデータベースがある場合には、 （スタートアップを簡単にするため）通常いくつかの設定ファイル内で、いくつかの `<session-factory>` を使う設定にしてください。

最初の4つの `property` 要素は JDBC コネクションに必要な設定を含んでいます。 dialect という名前の `property` 要素は、 Hibernate が生成する特定の SQL 方言を指定します。

## ティップ

In most cases, Hibernate is able to properly determine which dialect to use. See 「Dialect resolution」 for more information.

永続的なコンテキストに対する Hibernate のセッションの自動管理は、後の例ですぐにわかるように、役に立つことでしょう。 `hbm2ddl.auto` オプションはデータベーススキーマの自動生成を on にします。これは直接データベースに対して生成されます。当然（config オプションを削除して） off にしたり、 `SchemaExport` という Ant タスクの助けを借りてファイルにリダイレクトしたりできます。最後に永続クラスのためのマッピングファイルを設定に追加します。

Save this file as `hibernate.cfg.xml` into the `src/main/resources` directory.

### 1.1.5. Maven によるビルド

We will now build the tutorial with Maven. You will need to have Maven installed; it is available from the Maven download page. Maven will read the `/pom.xml` file we created earlier and know how to perform some basic project tasks. First, lets run the `compile` goal to make sure we can compile everything so far:

    [hibernateTutorial]$ mvn compile
    [INFO] Scanning for projects...
    [INFO] ------------------------------------------------------------------------
    [INFO] Building First Hibernate Tutorial
    [INFO]    task-segment: [compile]
    [INFO] ------------------------------------------------------------------------
    [INFO] [resources:resources]
    [INFO] Using default encoding to copy filtered resources.
    [INFO] [compiler:compile]
    [INFO] Compiling 1 source file to /home/steve/projects/sandbox/hibernateTutorial/target/classes
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 2 seconds
    [INFO] Finished at: Tue Jun 09 12:25:25 CDT 2009
    [INFO] Final Memory: 5M/547M
    [INFO] ------------------------------------------------------------------------

### 1.1.6. スタートアップとヘルパ

さて `Event` オブジェクトをロードしたり格納したりする準備ができました。しかしまずはインフラストラクチャのコードを書いて、セットアップを完了する必要があります。まずは Hibernate をスタートアップしなければなりません。このスタートアップには、グローバルの `SessionFactory` オブジェクトを生成して、それをアプリケーションのコードでアクセスしやすい場所に格納することが含まれます。 `org.hibernate.SessionFactory` は新しく `org.hibernate.Session` をオープンすることができます。 `org.hibernate.Session` はシングルスレッドの作業単位(Unit of Work)を表現します。それに対し `org.hibernate.SessionFactory` はスレッドセーフのグローバルオブジェクトであり、一度だけインスタンス化されます。

ここでスタートアップを行い、便利に `org.hibernate.SessionFactory` へアクセスする `HibernateUtil` ヘルパクラスを作成します。実装を見てみましょう：

    package org.hibernate.tutorial.util;

    import org.hibernate.SessionFactory;

    import org.hibernate.cfg.Configuration;

    public class HibernateUtil {

        private static final SessionFactory sessionFactory = buildSessionFactory();

        private static SessionFactory buildSessionFactory() {

            try {

                // Create the SessionFactory from hibernate.cfg.xml

                return new Configuration().configure().buildSessionFactory();

            }

            catch (Throwable ex) {

                // Make sure you log the exception, as it might be swallowed

                System.err.println("Initial SessionFactory creation failed." + ex);

                throw new ExceptionInInitializerError(ex);

            }

        }

        public static SessionFactory getSessionFactory() {

            return sessionFactory;

        }

    }

Save this code as `src/main/java/org/hibernate/tutorial/util/HibernateUtil.java`

This class not only produces the global `org.hibernate.SessionFactory` reference in its static initializer; it also hides the fact that it uses a static singleton. We might just as well have looked up the `org.hibernate.SessionFactory` reference from JNDI in an application server or any other location for that matter.

If you give the `org.hibernate.SessionFactory` a name in your configuration, Hibernate will try to bind it to JNDI under that name after it has been built. Another, better option is to use a JMX deployment and let the JMX-capable container instantiate and bind a `HibernateService` to JNDI. Such advanced options are discussed later.

You now need to configure a logging system. Hibernate uses commons logging and provides two choices: Log4j and JDK 1.4 logging. Most developers prefer Log4j: copy `log4j.properties` from the Hibernate distribution in the `etc/` directory to your `src` directory, next to `hibernate.cfg.xml`. If you prefer to have more verbose output than that provided in the example configuration, you can change the settings. By default, only the Hibernate startup message is shown on stdout.

チュートリアルのインフラは完全です。 Hibernate を使って実際の作業をする準備が整いました。

### 1.1.7. オブジェクトのロードと格納

We are now ready to start doing some real work with Hibernate. Let's start by writing an `EventManager` class with a `main()` method:

    package org.hibernate.tutorial;

    import org.hibernate.Session;

    import java.util.*;

    import org.hibernate.tutorial.domain.Event;

    import org.hibernate.tutorial.util.HibernateUtil;

    public class EventManager {

        public static void main(String[] args) {

            EventManager mgr = new EventManager();

            if (args[0].equals("store")) {

                mgr.createAndStoreEvent("My Event", new Date());

            }

            HibernateUtil.getSessionFactory().close();

        }

        private void createAndStoreEvent(String title, Date theDate) {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            session.beginTransaction();

            Event theEvent = new Event();

            theEvent.setTitle(title);

            theEvent.setDate(theDate);

            session.save(theEvent);

            session.getTransaction().commit();

        }

    }

In `createAndStoreEvent()` we created a new `Event` object and handed it over to Hibernate. At that point, Hibernate takes care of the SQL and executes an `INSERT` on the database.

A org.hibernate.Session is designed to represent a single unit of work (a single atomic piece of work to be performed). For now we will keep things simple and assume a one-to-one granularity between a Hibernate org.hibernate.Session and a database transaction. To shield our code from the actual underlying transaction system we use the Hibernate `org.hibernate.Transaction` API. In this particular case we are using JDBC-based transactional semantics, but it could also run with JTA.

What does `sessionFactory.getCurrentSession()` do? First, you can call it as many times and anywhere you like once you get hold of your `org.hibernate.SessionFactory`. The `getCurrentSession()` method always returns the "current" unit of work. Remember that we switched the configuration option for this mechanism to "thread" in our `src/main/resources/hibernate.cfg.xml`? Due to that setting, the context of a current unit of work is bound to the current Java thread that executes the application.

## 重要項目

Hibernate offers three methods of current session tracking. The "thread" based method is not intended for production use; it is merely useful for prototyping and tutorials such as this one. Current session tracking is discussed in more detail later on.

A org.hibernate.Session begins when the first call to `getCurrentSession()` is made for the current thread. It is then bound by Hibernate to the current thread. When the transaction ends, either through commit or rollback, Hibernate automatically unbinds the org.hibernate.Session from the thread and closes it for you. If you call `getCurrentSession()` again, you get a new org.hibernate.Session and can start a new unit of work.

作業単位 (Unit of Work) の範囲に関して、 Hibernate の org.hibernate.Session は1つまたはいくつかのデータベースオペレーションを実行するために使用されるべきでしょうか？上記の例は、１つのオペレーションで1つの org.hibernate.Session を使用します。これは純粋な偶然で、例はその他のアプローチを示すほど込み入っていません。 Hibernate の org.hibernate.Session の範囲は柔軟ですが、 全ての データベースオペレーションのために新しい Hibernate org.hibernate.Session を使用するようにアプリケーションをデザインするべきではありません。従って、もしそれを以下の (普通の) 例で何度か見たとしても、アンチパターンである オペレーション毎の Session を考慮してください。実際の (ウェブ) アプリケーションは、このチュートリアルで後に見ることができます。

See 13章Transactions and Concurrency for more information about transaction handling and demarcation. The previous example also skipped any error handling and rollback.

To run this, we will make use of the Maven exec plugin to call our class with the necessary classpath setup: `mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="store"`

## 注意

You may need to perform `mvn compile` first.

コンパイルすると、 Hibernate がスタートし、設定によりますが、多くのログ出力があるはずです。その最後には以下の行があるでしょう：

    [java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)

This is the `INSERT` executed by Hibernate.

To list stored events an option is added to the main method:

            if (args[0].equals("store")) {

                mgr.createAndStoreEvent("My Event", new Date());

            }

            else if (args[0].equals("list")) {

                List events = mgr.listEvents();

                for (int i = 0; i < events.size(); i++) {

                    Event theEvent = (Event) events.get(i);

                    System.out.println(

                            "Event: " + theEvent.getTitle() + " Time: " + theEvent.getDate()

                    );

                }

            }

新しい `listEvents()メソッド` も追加します。

        private List listEvents() {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            session.beginTransaction();

            List result = session.createQuery("from Event").list();

            session.getTransaction().commit();

            return result;

        }

Here, we are using a Hibernate Query Language (HQL) query to load all existing `Event` objects from the database. Hibernate will generate the appropriate SQL, send it to the database and populate `Event` objects with the data. You can create more complex queries with HQL. See 16章HQL: Hibernate クエリ言語 for more information.

Now we can call our new functionality, again using the Maven exec plugin: `mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="list"`

## 1.2. パート2 - 関連のマッピング

永続エンティティクラスをテーブルにマッピングしました。さらにこの上にいくつかのクラスの関連を追加しましょう。まず初めにアプリケーションに人々を追加し、彼らが参加するイベントのリストを格納します。

### 1.2.1. Person クラスのマッピング

最初の `Person` クラスは単純です：

    package org.hibernate.tutorial.domain;

    public class Person {

        private Long id;

        private int age;

        private String firstname;

        private String lastname;

        public Person() {}

        // Accessor methods for all properties, private setter for 'id'

    }

Save this to a file named `src/main/java/org/hibernate/tutorial/domain/Person.java`

Next, create the new mapping file as `src/main/resources/org/hibernate/tutorial/domain/Person.hbm.xml`

    <hibernate-mapping package="org.hibernate.tutorial.domain">

        <class name="Person" table="PERSON">

            <id name="id" column="PERSON_ID">

                <generator class="native"/>

            </id>

            <property name="age"/>

            <property name="firstname"/>

            <property name="lastname"/>

        </class>

    </hibernate-mapping>

最後に Hibernate の設定に新しいマッピングを追加してください：

    <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>

    <mapping resource="org/hibernate/tutorial/domain/Person.hbm.xml"/>

それではこれら2つのエンティティ間の関連を作成します。人々がイベントに参加でき、イベントが参加者を持つのは明らかです。扱わなければならない設計の問題は、方向、多重度、コレクションの振る舞いです。

### 1.2.2. 単方向 Set ベース関連

By adding a collection of events to the `Person` class, you can easily navigate to the events for a particular person, without executing an explicit query - by calling `Person#getEvents`. Multi-valued associations are represented in Hibernate by one of the Java Collection Framework contracts; here we choose a `java.util.Set` because the collection will not contain duplicate elements and the ordering is not relevant to our examples:

    public class Person {

        private Set events = new HashSet();

        public Set getEvents() {

            return events;

        }

        public void setEvents(Set events) {

            this.events = events;

        }

    }

Before mapping this association, let's consider the other side. We could just keep this unidirectional or create another collection on the `Event`, if we wanted to be able to navigate it from both directions. This is not necessary, from a functional perspective. You can always execute an explicit query to retrieve the participants for a particular event. This is a design choice left to you, but what is clear from this discussion is the multiplicity of the association: "many" valued on both sides is called a many-to-many association. Hence, we use Hibernate's many-to-many mapping:

    <class name="Person" table="PERSON">

        <id name="id" column="PERSON_ID">

            <generator class="native"/>

        </id>

        <property name="age"/>

        <property name="firstname"/>

        <property name="lastname"/>

        <set name="events" table="PERSON_EVENT">

            <key column="PERSON_ID"/>

            <many-to-many column="EVENT_ID" class="Event"/>

        </set>

    </class>

Hibernate はありとあらゆる種類のコレクションマッピングをサポートしていますが、最も一般的なものが `set` です。 多対多関連（または n:m エンティティリレーションシップ）には、関連テーブルが必要です。このテーブルのそれぞれの行は、人とイベント間のリンクを表現します。テーブル名は `set` 要素の `table` 属性で設定します。人側の関連の識別子カラム名は `key` 要素で、イベント側のカラム名は `many-to-many` の `column` 属性で定義します。 Hibernate にコレクションのオブジェクトのクラス （正確には、参照のコレクションの反対側のクラス）を教えなければなりません。

そのためこのマッピングのデータベーススキーマは以下のようになります：

        _____________        __________________
       |             |      |                  |       _____________
       |   EVENTS    |      |   PERSON_EVENT   |      |             |
       |_____________|      |__________________|      |    PERSON   |
       |             |      |                  |      |_____________|
       | *EVENT_ID   | <--> | *EVENT_ID        |      |             |
       |  EVENT_DATE |      | *PERSON_ID       | <--> | *PERSON_ID  |
       |  TITLE      |      |__________________|      |  AGE        |
       |_____________|                                |  FIRSTNAME  |
                                                      |  LASTNAME   |
                                                      |_____________|

### 1.2.3. 関連を働かせる

`EventManager` の新しいメソッドで人々とイベントを一緒にしましょう：

        private void addPersonToEvent(Long personId, Long eventId) {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            session.beginTransaction();

            Person aPerson = (Person) session.load(Person.class, personId);

            Event anEvent = (Event) session.load(Event.class, eventId);

            aPerson.getEvents().add(anEvent);

            session.getTransaction().commit();

        }

After loading a `Person` and an `Event`, simply modify the collection using the normal collection methods. There is no explicit call to `update()` or `save()`; Hibernate automatically detects that the collection has been modified and needs to be updated. This is called automatic dirty checking. You can also try it by modifying the name or the date property of any of your objects. As long as they are in persistent state, that is, bound to a particular Hibernate `org.hibernate.Session`, Hibernate monitors any changes and executes SQL in a write-behind fashion. The process of synchronizing the memory state with the database, usually only at the end of a unit of work, is called flushing. In our code, the unit of work ends with a commit, or rollback, of the database transaction.

異なる作業単位 (Unit of Work) で人々とイベントをロードすることも当然できます。そうでなければ、永続状態にないとき（以前に永続であったなら、この状態を 分離（detached） と呼びます）、 `org.hibernate.Session` の外部でオブジェクトを修正します。分離されるときにはコレクションを変更することも可能です：

        private void addPersonToEvent(Long personId, Long eventId) {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            session.beginTransaction();

            Person aPerson = (Person) session

                    .createQuery("select p from Person p left join fetch p.events where p.id = :pid")

                    .setParameter("pid", personId)

                    .uniqueResult(); // Eager fetch the collection so we can use it detached

            Event anEvent = (Event) session.load(Event.class, eventId);

            session.getTransaction().commit();

            // End of first unit of work

            aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached

            // Begin second unit of work

            Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();

            session2.beginTransaction();

            session2.update(aPerson); // Reattachment of aPerson

            session2.getTransaction().commit();

        }

`update` の呼び出しは分離オブジェクトを再び永続化します。これは、新しい作業単位 (Unit of Work) にバインドすると言えるでしょう。そのため分離の間に加えられたどのような修正もデータベースにセーブできます。エンティティオブジェクトのコレクションへの修正（追加･削除）も同様にセーブできます。

これは今はあまり使いみちがありませんが、自分のアプリケーションの設計に組み込むことができる重要なコンセプトです。それではこのエクササイズの最後に、 `EventManager` のメインメソッドに新しいアクションを追加してコマンドラインから呼び出してみましょう。人やイベントの識別子が必要なら、 `save()` メソッドが返してくれます （場合によっては識別子を返すためにメソッドを修正する必要があるかもしれません）。

            else if (args[0].equals("addpersontoevent")) {

                Long eventId = mgr.createAndStoreEvent("My Event", new Date());

                Long personId = mgr.createAndStorePerson("Foo", "Bar");

                mgr.addPersonToEvent(personId, eventId);

                System.out.println("Added person " + personId + " to event " + eventId);

            }

これは同じように重要な2つのクラス、つまり2つのエンティティ間の関連の例でした。前に述べたように、典型的なモデルには、普通「比較的重要ではない」他のクラスと型があります。これまでに見たような `int` や `java.lang.String` のようなものです。このようなクラスを 値型 と言います。このインスタンスは特定のエンティティに 依存 します。この型のインスタンスは独自の ID を持ちませんし、エンティティ間で共有されることもありません （ファーストネームが同じだったとしても、2人の人は同じ `firstname` オブジェクトを参照しません）。値型はもちろん JDK 内に見つかりますが、それだけではなく （実際、 Hibernate アプリケーションにおいてすべての JDK クラスは値型と見なせます）、 例えば `Address` や `MonetaryAmount` のような独自の依存クラスを書くこともできます。

値型のコレクションを設計することもできます。これは他のエンティティへの参照のコレクションとは概念的に非常に異なりますが、 Java ではほとんど同じように見えます。

### 1.2.4. 値のコレクション

Let's add a collection of email addresses to the `Person` entity. This will be represented as a `java.util.Set` of `java.lang.String` instances:

        private Set emailAddresses = new HashSet();

        public Set getEmailAddresses() {

            return emailAddresses;

        }

        public void setEmailAddresses(Set emailAddresses) {

            this.emailAddresses = emailAddresses;

        }

この `Set` のマッピングです：

            <set name="emailAddresses" table="PERSON_EMAIL_ADDR">

                <key column="PERSON_ID"/>

                <element type="string" column="EMAIL_ADDR"/>

            </set>

前のマッピングと比べて違うのは `element` の部分ですが、 Hibernate にこのコレクションが他のエンティティへの参照を含まず、 `string` 型の要素のコレクションを含むことを教えます（小文字の名前 (string) は Hibernate のマッピング型またはコンバータであるということです）。繰り返しますが、`set` 要素の `table` 属性は、コレクションのためのテーブル名を指定します。 `key` 要素はコレクションテーブルの外部キーカラム名を定義します。 `element` 要素の `column` 属性は `string` の値が実際に格納されるカラムの名前を定義します。

更新したスキーマを見てください：

      _____________        __________________
     |             |      |                  |       _____________
     |   EVENTS    |      |   PERSON_EVENT   |      |             |       ___________________
     |_____________|      |__________________|      |    PERSON   |      |                   |
     |             |      |                  |      |_____________|      | PERSON_EMAIL_ADDR |
     | *EVENT_ID   | <--> | *EVENT_ID        |      |             |      |___________________|
     |  EVENT_DATE |      | *PERSON_ID       | <--> | *PERSON_ID  | <--> |  *PERSON_ID       |
     |  TITLE      |      |__________________|      |  AGE        |      |  *EMAIL_ADDR      |
     |_____________|                                |  FIRSTNAME  |      |___________________|
                                                    |  LASTNAME   |
                                                    |_____________|

コレクションテーブルの主キーは、実際は両方のカラムを使った複合キーであることがわかります。これは人ごとに E メールアドレスが重複できないということで、 Java の set に要求されるセマンティクスそのものです。

以前人とイベントを関連づけたときと全く同じように、今や試しにコレクションに要素を追加することができるようになりました。 両方とも Java では同じコードです。

        private void addEmailToPerson(Long personId, String emailAddress) {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            session.beginTransaction();

            Person aPerson = (Person) session.load(Person.class, personId);

            // adding to the emailAddress collection might trigger a lazy load of the collection

            aPerson.getEmailAddresses().add(emailAddress);

            session.getTransaction().commit();

        }

This time we did not use a fetch query to initialize the collection. Monitor the SQL log and try to optimize this with an eager fetch.

### 1.2.5. 双方向関連

次に双方向関連をマッピングします。 Java で両側から人とイベントの関連を動作させます。もちろん、データベーススキーマは変わりませんが、多重度は多対多のままです。

## 注意

リレーショナルデータベースはネットワークプログラミング言語よりも柔軟なので、ナビゲーションの方向のようなものを必要としません。データはあらゆる方法で見たり復元できるということです。

まず `Event` イベントクラスに参加者のコレクションを追加します：

        private Set participants = new HashSet();

        public Set getParticipants() {

            return participants;

        }

        public void setParticipants(Set participants) {

            this.participants = participants;

        }

それでは `Event.hbm.xml` で関連のこちら側をマッピングしてください。

            <set name="participants" table="PERSON_EVENT" inverse="true">

                <key column="EVENT_ID"/>

                <many-to-many column="PERSON_ID" class="Person"/>

            </set

    >

ご覧のとおり、いずれのマッピングドキュメント (XMLファイル) でも、普通の `set` マッピングを使っています。 `key` と `many-to-many` のカラム名が、両方のマッピングドキュメントで入れ替えになっていることに注目してください。ここで最も重要な追加項目は、 `Event` のコレクションマッピングの `set` 要素にある `inverse="true"` 属性です。

この指定の意味は、2つの間のエンティティ間のリンクについての情報を探す必要があるとき、 Hibernate は反対側のエンティティ、つまり `Person` クラスから探すということです。一度2つのエンティティ間の双方向リンクがどのように作成されるかがわかれば、これを理解することはとても簡単です。

### 1.2.6. 双方向リンクの動作

まず、 Hibernate が通常の Java のセマンティクスに影響を及ぼさないことを心に留めておいてください。私たちは、単方向の例としてどのように `Person` と `Event` の間のリンクを作成したでしょうか？ `Person` のインスタンスのイベントへの参照のコレクションに `Event` のインスタンスを追加しました。そのためこのリンクを双方向にしたければ、当たり前ですが反対側にも同じことをしなければなりません。 `Event` のコレクションに `Person` への参照を追加するということです。この「両側でリンクを設定すること」は絶対に必要なので、決して忘れないでください。

多くの開発者は慎重にプログラムするので、エンティティの両側に正しく関連を設定するリンク管理メソッドを作成します。例えば `Person` では以下のようになります。：

        protected Set getEvents() {

            return events;

        }

        protected void setEvents(Set events) {

            this.events = events;

        }

        public void addToEvent(Event event) {

            this.getEvents().add(event);

            event.getParticipants().add(this);

        }

        public void removeFromEvent(Event event) {

            this.getEvents().remove(event);

            event.getParticipants().remove(this);

        }

コレクションのゲットとセットメソッドが現在 protected になっていることに注意してください。これは同じパッケージのクラスやサブクラスのメソッドは依然アクセスが可能ですが、 （ほとんど） そのパッケージ外のどのクラスでも直接そのコレクションを台無しにすることを防ぎます。おそらく反対側のコレクションにも同じことをした方がいいでしょう。

`inverse` マッピング属性とはいったい何でしょうか？開発者と Java にとっては、双方向リンクは単に両側の参照を正しく設定するということです。しかし Hibernate は（制約違反を避けるために） SQL の `INSERT` と `UPDATE` 文を正確に変更するための十分な情報を持っていないので、双方向関連プロパティを扱うための何らかの助けを必要とします。関連の片側を `inverse` に設定することで、 Hibernate は基本的には設定した側を無視し、反対側の 鏡 として考えます。これだけで、 Hibernate は方向を持つナビゲーションモデルを SQL データベーススキーマへ変換するときのすべての問題にうまく対処できます。覚えておかなければならないルールは簡単です。双方向関連は必ず片側を `inverse` にする必要があるということです。一対多関連ではそれは多側でなければなりません。多対多関連ではどちら側でも構いません。どちらでも違いはありません。

## 1.3. パート3 - EventManager Web アプリケーション

Hibernate の Web アプリケーションは、スタンドアローンのアプリケーションのように `Session` と `Transaction` を使用します。しかしいくつかの一般的なパターンが役立ちます。ここで `EventManagerServlet` を作成します。このサーブレットは、データベースに格納した全てのイベントをリストにでき、さらに HTML フォームから新しいイベントを入力できるものです。

### 1.3.1. 基本的な Servlet の記述

Servlet は HTTP の `GET` リクエストのみを処理するので、 `doGet()` を実装します。

    package org.hibernate.tutorial.web;

    // Imports

    public class EventManagerServlet extends HttpServlet {

        protected void doGet(

                HttpServletRequest request,

                HttpServletResponse response) throws ServletException, IOException {

            SimpleDateFormat dateFormatter = new SimpleDateFormat( "dd.MM.yyyy" );

            try {

                // Begin unit of work

                HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();

                // Process request and render page...

                // End unit of work

                HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();

            }

            catch (Exception ex) {

                HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().rollback();

                if ( ServletException.class.isInstance( ex ) ) {

                    throw ( ServletException ) ex;

                }

                else {

                    throw new ServletException( ex );

                }

            }

        }

    }

Save this servlet as `src/main/java/org/hibernate/tutorial/web/EventManagerServlet.java`

これは session-per-request というパターンです。 Servlet がリクエストを受け取ると、 `SessionFactory` の `getCurrentSession()` の最初の呼び出しで、 Hibernate の新しい `Session` が開かれます。そのときデータベーストランザクションが開始されます。データの読み書きに関わらず、すべてのデータアクセスはトランザクション内で行います（アプリケーション内ではオートコミットモードを使用しません）。

全てのデータベースオペレーションで新しい Hibernate `Session` を使用 しないでください 。全てのリクエストで機能する、１つの Hibernate `Session` を使用してください。自動的に現在の Java スレッドにバインドされるので、 `getCurrentSession()` を使用してください。

次に、リクエストのアクションは処理され、レスポンスである HTML が描画されます。これについてはすぐに説明します。

最後にリクエストの処理と HTML 描画が完了したときに、作業単位 (Unit of Work) を終了します。もし処理や描画中に問題が発生した場合、例外が送出されててデータベーストランザクションをロールバックします。これで `session-per-request` パターンが完了します。全てのサーブレットにトランザクション境界のコードを書く代わりに、サーブレットフィルタに記述することも可能です。 Open Session in View と呼ばれるこのパターンについては、 Hibernate の Web サイトや Wiki を参照してください。サーブレットではなく JSP で HTML 描画をしようとすると、すぐにこのパターンについての情報が必要になるでしょう。

### 1.3.2. 処理と描画

では、リクエストの処理とページの描画を実装します。

            // Write HTML header

            PrintWriter out = response.getWriter();

            out.println("<html><head><title>Event Manager</title></head><body>");

            // Handle actions

            if ( "store".equals(request.getParameter("action")) ) {

                String eventTitle = request.getParameter("eventTitle");

                String eventDate = request.getParameter("eventDate");

                if ( "".equals(eventTitle) || "".equals(eventDate) ) {

                    out.println("<b><i>Please enter event title and date.</i></b>");

                }

                else {

                    createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));

                    out.println("<b><i>Added event.</i></b>");

                }

            }

            // Print page

           printEventForm(out);

           listEvents(out, dateFormatter);

           // Write HTML footer

           out.println("</body></html>");

           out.flush();

           out.close();

Java と HTML が混在するコーディングスタイルは、より複雑なアプリケーションには適していないでしょう （このチュートリアルでは、基本的な Hibernate のコンセプトを示しているだけであることを覚えておいてください）。このコードは HTML のヘッダーとフッターの記述です。このページには、イベントを入力する HTML フォームと、データベースにある全てのイベントのリストが表示されます。最初のメソッドはごく単純な HTML 出力です。

        private void printEventForm(PrintWriter out) {

            out.println("<h2>Add new event:</h2>");

            out.println("<form>");

            out.println("Title: <input name='eventTitle' length='50'/><br/>");

            out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");

            out.println("<input type='submit' name='action' value='store'/>");

            out.println("</form>");

        }

`listEvents()` メソッドは、現在のスレッドに結びつく Hibernate の `Session` を使用して、クエリを実行します。

        private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {

            List result = HibernateUtil.getSessionFactory()

                    .getCurrentSession().createCriteria(Event.class).list();

            if (result.size() > 0) {

                out.println("<h2>Events in database:</h2>");

                out.println("<table border='1'>");

                out.println("<tr>");

                out.println("<th>Event title</th>");

                out.println("<th>Event date</th>");

                out.println("</tr>");

                Iterator it = result.iterator();

                while (it.hasNext()) {

                    Event event = (Event) it.next();

                    out.println("<tr>");

                    out.println("<td>" + event.getTitle() + "</td>");

                    out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");

                    out.println("</tr>");

                }

                out.println("</table>");

            }

        }

最後に、 `store` アクションが `createAndStoreEvent()` メソッドを呼び出します。このメソッドでも現在のスレッドの `Session` を利用します。

        protected void createAndStoreEvent(String title, Date theDate) {

            Event theEvent = new Event();

            theEvent.setTitle(title);

            theEvent.setDate(theDate);

            HibernateUtil.getSessionFactory()

                    .getCurrentSession().save(theEvent);

        }

これでサーブレットの完成です。サーブレットへのリクエストは、1つの `Session` と `Transaction` で処理されるでしょう。最初のスタンドアローンのアプリケーションのように、 Hibernate は自動的にこれらのオブジェクトを実行するスレッドに結び付けることができます。これにより、開発者が自由にコードをレイヤー分けでき、好きな方法で `SessionFactory` へのアクセスができるようになります。通常、開発者はより洗練されたデザインを使用して、データアクセスのコードをデータアクセスオブジェクトに移動するでしょう（DAOパターン）。より多くの例は、 Hibernate の Wiki を参照してください。

### 1.3.3. デプロイとテスト

To deploy this application for testing we must create a Web ARchive (WAR). First we must define the WAR descriptor as `src/main/webapp/WEB-INF/web.xml`

    <?xml version="1.0" encoding="UTF-8"?>

    <web-app version="2.4"

        xmlns="http://java.sun.com/xml/ns/j2ee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

        <servlet>

            <servlet-name>Event Manager</servlet-name>

            <servlet-class>org.hibernate.tutorial.web.EventManagerServlet</servlet-class>

        </servlet>

        <servlet-mapping>

            <servlet-name>Event Manager</servlet-name>

            <url-pattern>/eventmanager</url-pattern>

        </servlet-mapping>

    </web-app>

ビルドとデプロイのために、プロジェクトディレクトリで `mvn package` を呼び出し、 `hibernate-tutorial.war` ファイルを Tomcat の `webapp` ディレクトリにコピーしてください。

## 注意

If you do not have Tomcat installed, download it from http://tomcat.apache.org/ and follow the installation instructions. Our application requires no changes to the standard Tomcat configuration.

一度デプロイして Tomcat を起動すれば、 `http://localhost:8080/hibernate-tutorial/eventmanager` でアプリケーションへのアクセスが可能です。最初のリクエストが作成したサーブレットに渡ったときに、 Tomcat のログで Hibernate の初期化処理を確認してください （ `HibernateUtil` 内の静的初期化ブロックが呼ばれています）。また、例外が発生したなら詳細を確認してください。

## 1.4. 要約

このチュートリアルでは、簡単なスタンドアローンの Hibernate アプリケーションと小規模の Web アプリケーションを書くための基本を紹介しました。

製作著作 © 2004 Red Hat, Inc.

- 前のページ前書き
- 上に戻る
- ホーム
- 次のページ第2章 アーキテクチャ