Hibernate.orgCommunity Documentation
3.5.6-Final
製作著作 © 2004 Red Hat, Inc.
September 15, 2010
Working with object-oriented software and a relational database can be cumbersome and time consuming in today's enterprise environments. Hibernate is an Object/Relational Mapping tool for Java environments. The term Object/Relational Mapping (ORM) refers to the technique of mapping a data representation from an object model to a relational data model with a SQL-based schema.
Hibernate は Java クラスからデータベーステーブルへのマッピング(及び Java データタイプから SQL データタイプへのマッピング)を行うだけでなくデータのクエリや検索機能も提供するため、 SQL や JDBC での手作業によるデータ処理を除き開発に要する時間を大幅に削減することが可能になります。
Hibernate の目標は、 開発者にとってのプログラミングにおける一般的なデータ永続性の作業の 95 % を軽減することです。 Hibernate データベース内でビジネスロジックを実現するストアドプロシージャのみを使用するデータ処理中心のアプリケーションに対しては最適ではないかもしれませんが、 Java ベースの中間層でのビジネスロジック及びオブジェクト指向のドメインモデルを使用する場合に最も役に立ちます。 Hibernate は開発者がベンダー固有の SQL コードの除去あるいはカプセル化を行う際に便利なため、 表形式の表現からオブジェクトのグラフへの結果セットの変換に関する一般的な作業に役立ちます。
Hibernate 及びオブジェクト/リレーショナルマッピング、 あるいは Java が不慣れな方は、 次の手順を行ってください。
Read 1章Tutorial for a tutorial with step-by-step instructions. The source code for the tutorial is included in the distribution in the doc/reference/tutorial/ directory.
Read 2章アーキテクチャ to understand the environments where Hibernate can be used.
Hibernate ディストリビューション内の eg/ ディレクトリ内を見てください。 シンプルなスタンドアローンのアプリケーションが含まれています。 ご使用の JDBC ドライバを lib/ ディレクトリにコピーしてから使用するデータベースに対して正しい値を指定するよう etc/hibernate.properties を編集します。 ディストリビューションディレクトリ内のコマンドプロンプトから、 ant eg (Ant を使用)と入力するか、 Windows 環境の場合は build eg と入力します。
Use this reference documentation as your primary source of information. Consider reading [JPwH] if you need more help with application design, or if you prefer a step-by-step tutorial. Also visit http://caveatemptor.hibernate.org and download the example application from [JPwH].
よくある質問とその答え (FAQ) は Hibernate ウェブサイトでご覧ください。
Links to third party demos, examples, and tutorials are maintained on the Hibernate website.
Hibernate ウェブサイト上の Community Area はデザインのパターンやさまざまな統合ソリューション (Tomcat、 JBoss AS、 Struts、 EJB など)を検索する上で興味深いリソースになります。
質問がある場合は、 Hibernate ウェブサイト上にリンクされたユーザーフォーラムをご利用ください。 また、 バグ報告及び今後のリクエストに関しては JIRA (問題追跡システム) を提供しています。 Hibernate, の開発に興味がある方は、 開発者用メーリングリストにご参加ください。 本ドキュメントの翻訳に興味がある方は、 開発者用メーリングリストよりご連絡ください。
Hibernate に関する商業用開発サポート、 実稼働サポート、 トレーニングについては JBoss Inc よりご利用頂けます (http://www.hibernate.org/SupportTraining/ を参照)。 Hibernate はプロフェッショナルなオープンソースプロジェクトであり、 JBoss Enterprise Middleware System (JEMS) スィート製品の重要なコンポーネントになります。
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.
仮に小さなデータベースアプリケーションが必要だとしましょう。そのアプリケーションには出席したいイベントと、そのイベントのホストについての情報を格納するものとします。
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.
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.
次にデータベースに格納するイベントを表すクラスを作成します。
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.
Hibernate は、どのように永続クラスのオブジェクトをロードし格納すればよいかを知る必要があります。ここで Hibernate マッピングファイルが登場します。マッピングファイルは、データベース内のどのテーブルにアクセスしなければならないか、そのテーブルのどのカラムを使うべきかを、 Hibernate に教えます。
マッピングファイルの基本的な構造はこのようになります:
<?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 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.
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://hibernate.sourceforge.net/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.
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] ------------------------------------------------------------------------
さて 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 を使って実際の作業をする準備が整いました。
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つのオペレーションで1つの org.hibernate.Session を使用します。これは純粋な偶然で、例はその他のアプローチを示すほど込み入っていません。 Hibernate の org.hibernate.Session の範囲は柔軟ですが、 全ての データベースオペレーションのために新しい Hibernate org.hibernate.Session を使用するようにアプリケーションをデザインするべきではありません。従って、もしそれを以下の (普通の) 例で何度か見たとしても、アンチパターンである オペレーション毎の Session を考慮してください。実際の (ウェブ) アプリケーションは、このチュートリアルで後に見ることができます。
See 12章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 15章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"
永続エンティティクラスをテーブルにマッピングしました。さらにこの上にいくつかのクラスの関連を追加しましょう。まず初めにアプリケーションに人々を追加し、彼らが参加するイベントのリストを格納します。
最初の 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="events/Event.hbm.xml"/>
<mapping resource="events/Person.hbm.xml"/>
それではこれら2つのエンティティ間の関連を作成します。人々がイベントに参加でき、イベントが参加者を持つのは明らかです。扱わなければならない設計の問題は、方向、多重度、コレクションの振る舞いです。
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 |
|_____________|
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 ではほとんど同じように見えます。
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.
次に双方向関連をマッピングします。 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="events.Person"/>
</set
>
ご覧のとおり、いずれのマッピングドキュメント (XMLファイル) でも、普通の set マッピングを使っています。 key と many-to-many のカラム名が、両方のマッピングドキュメントで入れ替えになっていることに注目してください。ここで最も重要な追加項目は、 Event のコレクションマッピングの set 要素にある inverse="true" 属性です。
この指定の意味は、2つの間のエンティティ間のリンクについての情報を探す必要があるとき、 Hibernate は反対側のエンティティ、つまり Person クラスから探すということです。一度2つのエンティティ間の双方向リンクがどのように作成されるかがわかれば、これを理解することはとても簡単です。
まず、 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 にする必要があるということです。一対多関連ではそれは多側でなければなりません。多対多関連ではどちら側でも構いません。どちらでも違いはありません。
Hibernate の Web アプリケーションは、スタンドアローンのアプリケーションのように Session と Transaction を使用します。しかしいくつかの一般的なパターンが役立ちます。ここで EventManagerServlet を作成します。このサーブレットは、データベースに格納した全てのイベントをリストにでき、さらに HTML フォームから新しいイベントを入力できるものです。
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 を使用 しないでください 。全てのリクエストで機能する、1つの Hibernate Session を使用してください。自動的に現在の Java スレッドにバインドされるので、 getCurrentSession() を使用してください。
次に、リクエストのアクションは処理され、レスポンスである HTML が描画されます。これについてはすぐに説明します。
最後にリクエストの処理と HTML 描画が完了したときに、作業単位 (Unit of Work) を終了します。もし処理や描画中に問題が発生した場合、例外が送出されててデータベーストランザクションをロールバックします。これで session-per-request パターンが完了します。全てのサーブレットにトランザクション境界のコードを書く代わりに、サーブレットフィルタに記述することも可能です。 Open Session in View と呼ばれるこのパターンについては、 Hibernate の Web サイトや Wiki を参照してください。サーブレットではなく JSP で HTML 描画をしようとすると、すぐにこのパターンについての情報が必要になるでしょう。
では、リクエストの処理とページの描画を実装します。
// 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 を参照してください。
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 内の静的初期化ブロックが呼ばれています)。また、例外が発生したなら詳細を確認してください。
Hibernate アーキテクチャの(非常に)高いレベルからのビュー:

We do not have the scope in this document to provide a more detailed view of all the runtime architectures available; Hibernate is flexible and supports several different approaches. We will, however, show the two extremes: "minimal" architecture and "comprehensive" architecture.
この図は Hibernate が、アプリケーションに対して永続化サービス (と永続オブジェクト)を提供するために、データベースと設定データを使うことを示しています。
The "minimal" architecture has the application provide its own JDBC connections and manage its own transactions. This approach uses a minimal subset of Hibernate's APIs:

The "comprehensive" architecture abstracts the application away from the underlying JDBC/JTA APIs and allows Hibernate to manage the details.

以下は、図に含まれるオブジェクトの定義です:
org.hibernate.SessionFactory)A threadsafe, immutable cache of compiled mappings for a single database. A factory for Session and a client of ConnectionProvider, SessionFactory can hold an optional (second-level) cache of data that is reusable between transactions at a process, or cluster, level.
org.hibernate.Session)A single-threaded, short-lived object representing a conversation between the application and the persistent store. It wraps a JDBC connection and is a factory for Transaction. Session holds a mandatory first-level cache of persistent objects that are used when navigating the object graph or looking up objects by identifier.
永続化状態とビジネス機能を持つ、短命でシングルスレッドのオブジェクト。これは通常の JavaBeans/POJO のこともありますが、特徴的なことは、その時点での(ただ1つの) Session と関連していることです。 Session がクローズされるとすぐに、それらは切り離されて他のアプリケーション層から自由に使うことができます(例えばデータトランスファオブジェクトとして、プレゼンテーション層から、またはプレゼンテーション層へ直接使用できます)。
現時点では Session と関連していない、永続クラスのインスタンス。すでにアプリケーション側でインスタンス化されていて、まだ永続化されていないか、クローズされた Session でインスタンス化されたかのどちらかです。
org.hibernate.Transaction)(オプション) 原子性を持つ作業単位 (Unit of Work) を指定するために、アプリケーションが使用する、シングルスレッドで短命なオブジェクト。下に位置する JDBC 、 JTA 、 CORBA トランザクションからアプリケーションを抽象化します。 Session は、時にはいくつかの Transaction をまたがるかもしれません。しかし、下の層の API を使うにせよ、 Transaction を使うにせよ、トランザクション境界を設定することは、決してオプションではありません。
org.hibernate.connection.ConnectionProvider)(オプション) JDBC コネクション(とそのプール)のファクトリ。下の層に位置する Datasource や DriverManager からアプリケーションを抽象化します。アプリケーションには公開されませんが、開発者が継承または実装することは可能です。
org.hibernate.TransactionFactory)(オプション) Transaction インスタンスのファクトリ。アプリケーションには公開されませんが、開発者が継承または実装することは可能です。
Hibernate は、永続層の振る舞いをカスタマイズするために、多くのオプション拡張インタフェースを用意しています。詳細は API ドキュメントを参照してください。
「軽い」アーキテクチャでは、アプリケーションは直接 JTA や JDBC と対話するために、 Transaction や TransactionFactory や ConnectionProvider の API をバイパスします。
永続クラスのインスタンスは、次の3つの異なる状態のどれかになります。それは、 永続コンテキスト によって決まります。 Hibernate の Session オブジェクトが、永続コンテキストになります:
The instance is not associated with any persistence context. It has no persistent identity or primary key value.
この状態のインスタンスは、その時点で永続コンテキストに関連づいています。また、永続 ID (主キーの値)を持ち、たいていはデータベースに対応する行を持っているでしょう。特定の永続コンテキストのなかでは、永続 ID が Java の ID (オブジェクトのメモリ上の位置)と同じであることを Hibernate が 保証 します。
この状態のインスタンスは、かつて永続コンテキストに関連づけられたが、そのコンテキストがクローズされたか、あるいは、他のプロセスにそのインスタンスがシリアライズされたかです。このインスタンスは、永続 ID を持ち、たいていはデータベースに対応する行を持っているでしょう。分離インスタンスに対しては、永続 ID と Java の ID との関連は、 Hibernate が保証しません。
JMX は Java コンポーネント管理の J2EE 標準です。 JMX 標準サービスを通して、 Hibernate は管理されます。ディストリビューションの中に org.hibernate.jmx.HibernateService という MBean 実装を用意しています。
JBoss アプリケーションサーバー上に Hibernate を JMX サービスとしてデプロイする方法の例としては、 JBoss ユーザーガイドを参照してください。 JBoss アプリケーションサーバーにおいて、 JMX を使ってデプロイすると、次のメリットが得られます:
セッション管理: Hibernate の Session のライフサイクルは、自動的に JTA トランザクションのスコープに結びつけられます。これは、もはや手動で Session をオープンしたり、クローズしたりする必要がないことを意味します。これは、 JBoss EJB インターセプタの仕事になります。また、コードのどこでトランザクション境界を設定するかについて、もはや悩む必要がありません(もちろん移植可能な永続層を書かかなくていいのならば、オプションの Hibernate の Transaction を使用してください。) Session にアクセスするためには、 HibernateContext をコールしてください。
HAR デプロイ: 通常、( EAR または SAR ファイルにある) JBoss サービスデプロイメントディスクリプタを使って、 Hibernate JMX サービスをデプロイします。それは、 Hibernate の SessionFactory の全ての一般的な設定オプションをサポートします。しかし依然としてデプロイメントディスクリプタのなかにすべてのマッピングファイルの名前を挙げる必要があります。もし、オプションの HAR デプロイメントを使うことを決めたなら、 JBoss は自動的に HAR ファイルのなかの全てのマッピングファイルを検出します。
これらのオプションについての詳細な情報は、 JBoss アプリケーションサーバーユーザーガイドを参考にしてください。
Another feature available as a JMX service is runtime Hibernate statistics. See 「Hibernate 統計」 for more information.
Hibernate は JCA コネクタとしても設定できます。詳細については、 Web サイトを見てください。 Hibernate JCA サポートは、今のところ実験段階として考えられていることに注意してください。
Hibernate を使ったアプリケーションは、ほとんど、なんらかの形で「コンテキスト上の」セッションが必要になります。「コンテキスト上のセッション」は、特定のコンテキストのスコープのなかで有効なセッションのことです。しかし、通常アプリケーションごとにコンテキストを構成するものの定義は異なります。しかも、異なる複数のコンテキストは、現時点に対して異なるスコープを定義します。バージョン3.0より前の Hibernate では、自作の ThreadLocal ベースの「コンテキスト上のセッション」を利用するか、 HibernateUtil のようなヘルパークラスを利用するか、 proxy/interception ベースの「コンテキスト上のセッション」を提供する (Spring や Pico のような)サードパーティのフレームワークを利用するかのいずれかでした。
Starting with version 3.0.1, Hibernate added the SessionFactory.getCurrentSession() method. Initially, this assumed usage of JTA transactions, where the JTA transaction defined both the scope and context of a current session. Given the maturity of the numerous stand-alone JTA TransactionManager implementations, most, if not all, applications should be using JTA transaction management, whether or not they are deployed into a J2EE container. Based on that, the JTA-based contextual sessions are all you need to use.
しかし、バージョン 3.1 からは、 SessionFactory.getCurrentSession() の後の処理が、プラガブルになりました。これを受けて、現在のセッションを定義するスコープとコンテキストのプラガビリティを可能にするために、新しい拡張インタフェース ( org.hibernate.context.CurrentSessionContext ) と新しい構成パラメータ ( hibernate.current_session_context_class ) が追加されました。
See the Javadocs for the org.hibernate.context.CurrentSessionContext interface for a detailed discussion of its contract. It defines a single method, currentSession(), by which the implementation is responsible for tracking the current contextual session. Out-of-the-box, Hibernate comes with three implementations of this interface:
org.hibernate.context.JTASessionContext - JTA トランザクションによって、現在のセッションが追跡され、スコープを決められます。この処理は、古い JTA だけのアプローチとまったく同じです。詳細は Javadoc を参照してください。
org.hibernate.context.ThreadLocalSessionContext - スレッドの実行によって、現在のセッションが追跡されます。詳細は Javadoc を参照してください。
org.hibernate.context.ManagedSessionContext - スレッドの実行によって、現在のセッションが追跡されます。しかし、このクラスの static メソッドで Session インスタンスをバインド/アンバインドする責任はあなたにあります。これは決して Session をオープン、フラッシュ、クローズしません。
The first two implementations provide a "one session - one database transaction" programming model. This is also known and used as session-per-request. The beginning and end of a Hibernate session is defined by the duration of a database transaction. If you use programmatic transaction demarcation in plain JSE without JTA, you are advised to use the Hibernate Transaction API to hide the underlying transaction system from your code. If you use JTA, you can utilize the JTA interfaces to demarcate transactions. If you execute in an EJB container that supports CMT, transaction boundaries are defined declaratively and you do not need any transaction or session demarcation operations in your code. Refer to 12章Transactions and Concurrency for more information and code examples.
hibernate.current_session_context_class 設定パラメータは、 org.hibernate.context.CurrentSessionContext のどの実装を使うかを指定します。下位互換性のため、このパラメータが設定されず org.hibernate.transaction.TransactionManagerLookup が設定されていた場合、 Hibernate は org.hibernate.context.JTASessionContext を使うことに注意してください。通常このパラメータの値には、3つの実装の中から使用する実装クラスの名前を直接指定します。しかし、"jta"、 "thread"、 "managed"というそれぞれの省略名も用意されています。
Hibernate は様々な環境で動作するようにデザインされているため、非常に多くの設定要素があります。幸いなことに、 Hibernate は、公開されているパッケージの etc/ フォルダの hibernate.properties に、ほとんどの設定要素の適切なデフォルト値が記述されています。この hibernate.properties をクラスパスに設定し、設定要素をカスタマイズするだけです。
org.hibernate.cfg.Configuration のインスタンスは、 Java の型と SQL データベースのマッピング情報をすべて持っています。 Configuration は、(不変の) SessionFactory を生成するときに使用します。複数の XML マッピングファイルを変換し、マッピング情報にします。
通常、 org.hibernate.cfg.Configuration インスタンスは、特定の XML マッピングファイルによって直接初期化されます。もし、マッピングファイルがクラスパスに設定されている場合、次のメソッドを使ってください。 addResource() :
Configuration cfg = new Configuration()
.addResource("Item.hbm.xml")
.addResource("Bid.hbm.xml");
代替案 (こちらのほうが良いときもあります) としてマッピングクラスを指定する方法もあります。 Hibernate に、マッピングファイルを 見つけさせてください:
Configuration cfg = new Configuration()
.addClass(org.hibernate.auction.Item.class)
.addClass(org.hibernate.auction.Bid.class);
Hibernate は、クラスパスにある以下のような名前のマッピングファイルを見つけます。 /org/hibernate/auction/Item.hbm.xml 、 /org/hibernate/auction/Bid.hbm.xml 。この方法だと、ハードコーディングされたファイル名を排除できます。
org.hibernate.cfg.Configuration は、設定プロパティを指定することもできます:
Configuration cfg = new Configuration()
.addClass(org.hibernate.auction.Item.class)
.addClass(org.hibernate.auction.Bid.class)
.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
.setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
.setProperty("hibernate.order_updates", "true");
Hibernate に設定プロパティを渡す方法は1つではありません。さまざまなオプションを用意しています:
java.util.Properties インスタンスを Configuration.setProperties() に渡します。
hibernate.properties をクラスパスのルートディレクトリに置きます。
System プロパティが java -Dproperty=value を使うように設定します。
<property> 要素を hibernate.cfg.xml (後述)に設定します。
If you want to get started quicklyhibernate.properties is the easiest approach.
org.hibernate.cfg.Configuration は、起動時にだけあるオブジェクトであり、一度 SessionFactory を生成した後は、破棄されることを意図しています。
When all mappings have been parsed by the org.hibernate.cfg.Configuration, the application must obtain a factory for org.hibernate.Session instances. This factory is intended to be shared by all application threads:
SessionFactory sessions = cfg.buildSessionFactory();
Hibernate does allow your application to instantiate more than one org.hibernate.SessionFactory. This is useful if you are using more than one database.
通常、開発者は org.hibernate.SessionFactory を生成し、 SessionFactory で JDBC コネクションをプーリングしたいと考えます。そのアプローチを採用する場合、単純に org.hibernate.Session をオープンしてください:
Session session = sessions.openSession(); // open a new Session
これだけで、プーリングした JDBC コネクションを使って目的のデータベースにアクセスすることができます。
そのためには、 JDBC コネクションのプロパティを Hibernate に設定する必要があります。すべての Hibernate プロパティ名とセマンティクスは org.hibernate.cfg.Environment クラスに定義されています。この設定は JDBC コネクション設定の中で一番重要なものです。
もし、以下のプロパティを設定すると、 Hibernate はコネクションを取得するために(プールも) java.sql.DriverManager を使います:
表3.1 Hibernate JDBC プロパティ
| プロパティ名 | 意味 |
|---|---|
| hibernate.connection.driver_class | JDBC driver class |
| hibernate.connection.url | JDBC URL |
| hibernate.connection.username | database user |
| hibernate.connection.password | database user password |
| hibernate.connection.pool_size | maximum number of pooled connections |
Hibernate のコネクションプールアルゴリズムは非常に初歩的なものです。これはすぐに始められるようにと用意されたもので、 製品として使用することを意図していません 。また、パフォーマンスのテストのためのものでもありません。最高のパフォーマンスと安定性を持ったプールを実現したければ、サードパーティのツールをお勧めします。 hibernate.connection.pool_size プロパティと適切なコネクションプールの設定を置き換えてください。これにより Hibernate のインターナルプールを無効にします。例えば次のように C3P0 を使います。
C3P0 はオープンソース JDBC コネクションプールで、 Hibernate の lib ディレクトリにあります。もし、 hibernate.c3p0.* プロパティをセットすれば、 Hibernate は、 C3P0ConnectionProvider を使います。もし Proxool を使いたい場合は、 hibernate.properties パッケージを参照したり、 Hibernate の Web サイトでより多くの情報を取得してください。
C3P0 用の hibernate.properties ファイルを例として示します:
hibernate.connection.driver_class = org.postgresql.Driver hibernate.connection.url = jdbc:postgresql://localhost/mydatabase hibernate.connection.username = myuser hibernate.connection.password = secret hibernate.c3p0.min_size=5 hibernate.c3p0.max_size=20 hibernate.c3p0.timeout=1800 hibernate.c3p0.max_statements=50 hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
アプリケーションサーバー上で使う場合は、 Hibernate を設定し、アプリケーションサーバーからコネクションを取得するようにしてください。 javax.sql.Datasource を JNDI に登録します。そしてプロパティを以下のように設定してください:
表3.2 Hibernate データソースプロパティ
| プロパティ名 | 意味 |
|---|---|
| hibernate.connection.datasource | datasource JNDI name |
| hibernate.jndi.url | JNDI プロバイダの URL (オプション) |
| hibernate.jndi.class | JNDI のクラス InitialContextFactory (オプション) |
| hibernate.connection.username | データベースユーザ (オプション) |
| hibernate.connection.password | データベースユーザのパスワード (オプション) |
アプリケーションサーバーから提供された JNDI データソースを使う hibernate.properties ファイルの例を示します:
hibernate.connection.datasource = java:/comp/env/jdbc/test
hibernate.transaction.factory_class = \
org.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class = \
org.hibernate.transaction.JBossTransactionManagerLookup
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialectJNDI データソースから取得した JDBC コネクションは、アプリケーションサーバーのコンテナ管理トランザクションに自動的に参加します。
任意のコネクションプロパティは、追加された "hibernate.connnection" プロパティ名によって与えられます。例えば、 charSet を設定したい場合は、 hibernate.connection.charSet を使います。
JDBC コネクションを取得する戦略を持つ独自のプラグインを定義する場合は、 org.hibernate.connection.ConnectionProvider インターフェースを実装してください。そして、実装クラスを hibernate.connection.provider_class に設定してください。
これらのプロパティはランタイムに Hibernate の挙動を制御するものです。これらのプロパティはすべて妥当なデフォルト値があり、任意で設定します。
Some of these properties are "system-level" only. System-level properties can be set only via java -Dproperty=value or hibernate.properties. They cannot be set by the other techniques described above.
表3.3 Hibernate 設定プロパティ
| プロパティ名 | 意味 |
|---|---|
| hibernate.dialect | Hibernate のクラス名 org.hibernate.dialect.Dialect が入ります。これはリレーショナルデータベースごとに最適化された SQL を生成します。 例 In most cases Hibernate will actually be able to choose the correct |
| hibernate.show_sql | 発行されたすべての SQL をコンソールに出力します。これはログカテゴリの org.hibernate.SQL に debug を設定する方法の代替手段です。 例 |
| hibernate.format_sql | ログとコンソールの SQL を整形して表示します。 例 |
| hibernate.default_schema | 生成される SQL 文のテーブルに設定するスキーマ/テーブルスペースです。 例. |
| hibernate.default_catalog | 生成される SQL 文のテーブルに設定するカタログです。 例 |
| hibernate.session_factory_name | org.hibernate.SessionFactory は生成後、この名前で JNDI に自動的に登録されます。 例 |
| hibernate.max_fetch_depth | 外部結合フェッチの最大深度を設定します。結合する関連は対一関連のみ(一対一、多対一)です。 0 を指定すると外部結合フェッチは無効になります。 例: 推奨する値は |
| hibernate.default_batch_fetch_size | 関連フェッチのデフォルト Hibernate バッチサイズを指定します。 例: 推奨する値は |
| hibernate.default_entity_mode | Sets a default mode for entity representation for all sessions opened from this SessionFactory
|
| hibernate.order_updates | 項目が更新されたときに、別の SQL で主キーを更新することを強制します。この場合、同時実行可能なシステムでは、まれにデッドロックが発生する可能性があります。 例 |
| hibernate.generate_statistics | 有効の場合、 Hibernate はパフォーマンスチューニングに有効な統計情報を収集します。 例 |
| hibernate.use_identifier_rollback | 有効の場合、オブジェクトが削除されたときに識別子プロパティをリセットし、デフォルト値にしたものを生成します。 例 |
| hibernate.use_sql_comments | 有効の場合、 SQL 内にコメントを生成します。これはデバックを容易にします。デフォルトの値は false です。 例 |
表3.4 Hibernate JDBC とコネクションプロパティ
| プロパティ名 | 意味 |
|---|---|
| hibernate.jdbc.fetch_size | 値が0でない場合、 JDBC フェッチサイズを決定します ( Statement.setFetchSize() を呼びます)。 |
| hibernate.jdbc.batch_size | 値が0でない場合、 Hibernate が JDBC2 バッチ更新を使用します。 例: 推奨する値は |
| hibernate.jdbc.batch_versioned_data | Set this property to true if your JDBC driver returns correct row counts from executeBatch(). It is usually safe to turn this option on. Hibernate will then use batched DML for automatically versioned data. Defaults to false. 例 |
| hibernate.jdbc.factory_class | カスタム org.hibernate.jdbc.Batcher を選びます。ほとんどのアプリケーションに、この設定プロパティは必要ありません。 例 |
| hibernate.jdbc.use_scrollable_resultset | Hibernate による JDBC2 のスクロール可能なリザルトセットの使用を有効にします。このプロパティは、ユーザーによって提供された JDBC コネクションを使用している場合のみ必要で、そうでなければ Hibernate はコネクションメタデータを使用します。 例 |
| hibernate.jdbc.use_streams_for_binary | JDBC へ/から binary や serializable の書き込み/読み込みストリームを使います (システムレベルのプロパティ)。 例 |
| hibernate.jdbc.use_get_generated_keys | 挿入の後に自動生成された主キーを取得するための JDBC3 PreparedStatement.getGeneratedKeys() の使用を有効にします。これは JDBC3+ ドライバと JRE1.4+ を必要とし、もし Hibernate の識別子ジェネレータに問題が発生するようなら false に設定してください。デフォルトではコネクションメタデータを使いドライバの能力を決定します。 例 |
| hibernate.connection.provider_class | JDBC コネクションを Hibernate に提供する独自の ConnectionProvider のクラス名。 例 |
| hibernate.connection.isolation | JDBC トランザクション分離レベルを設定します。妥当な値を調べるためには java.sql.Connection をチェックしてください。しかし使用するデータベースが、すべての分離レベルをサポートしているとは限りません。 例 |
| hibernate.connection.autocommit | プールされている JDBC コネクションの自動コミットを有効にする(非推奨)。 例 |
| hibernate.connection.release_mode | Hibernate がいつ JDBC コネクションをリリースするかを指定します。デフォルトではセッションが明示的にクローズまたは切断されてもコネクションは保持します。アプリケーションサーバーの JTA データソースの場合、すべての JDBC コールの後、強制的にコネクションをリリースするために after_statement を使ってください。非 JTA コネクションの場合、各トランザクションが終了したときに after_transaction を使い、コネクションをリリースしてください。 auto にすると、 JTA や CMT トランザクションの場合、 after_statement でクローズし、 JDBC トランザクションの場合、 after_transaction でクローズします。 例 This setting only affects |
| hibernate.connection.<propertyName> | JDBC プロパティ <propertyName> を DriverManager.getConnection() に渡します。 |
| hibernate.jndi.<propertyName> | プロパティ <propertyName> を JNDI InitialContextFactory に渡します。 |
表3.5 Hibernate キャッシュプロパティ
| プロパティ名 | 意味 |
|---|---|
hibernate.cache.provider_class
| カスタム CacheProvider のクラス名です。 例 |
hibernate.cache.use_minimal_puts
| 書き込みを最小限にするために、二次キャッシュの操作を最適化します。その代わりに、読み込みがより頻繁に発生するようになります。このセッティングはクラスタキャッシュで役に立ちます。 Hibernate3 ではクラスタキャッシュ実装用にデフォルトでは有効になっています。 例 |
hibernate.cache.use_query_cache
| 特定のクエリがキャッシュ可能な場合に、クエリキャッシュを有効にします。 例 |
hibernate.cache.use_second_level_cache
| 二次キャッシュを完全に無効にする場合に使います。デフォルトでは有効で、クラスの <cache> マッピングで制御します。 例 |
hibernate.cache.query_cache_factory
| カスタム QueryCache インターフェースのクラス名を指定します。デフォルトでは StandardQueryCache になります。 e.g. |
hibernate.cache.region_prefix
| 二次キャッシュの領域名の接頭辞です。 例 |
hibernate.cache.use_structured_entries
| 二次キャッシュに格納するデータを、人が理解しやすいフォーマットにします。 例 |
表3.6 Hibernate トランザクションプロパティ
| プロパティ名 | 意味 |
|---|---|
hibernate.transaction.factory_class
| Hibernate Transaction API と一緒に使われる TransactionFactory のクラス名です。 (デフォルトでは JDBCTransactionFactory です)。 例 |
jta.UserTransaction
| アプリケーションサーバーから JTA UserTransaction を取得するために JTATransactionFactory に使われる JNDI 名です。 例 |
hibernate.transaction.manager_lookup_class
| TransactionManagerLookup のクラス名です。 JTA 環境において、 JVM レベルのキャッシュを有効にするときか、 hilo ジェネレータが使用されるときに必要です。 例 |
hibernate.transaction.flush_before_completion
| If enabled, the session will be automatically flushed during the before completion phase of the transaction. Built-in and automatic session context management is preferred, see 「コンテキスト上のセッション」. 例 |
hibernate.transaction.auto_close_session
| If enabled, the session will be automatically closed during the after completion phase of the transaction. Built-in and automatic session context management is preferred, see 「コンテキスト上のセッション」. 例 |
表3.7 その他のプロパティ
| プロパティ名 | 意味 |
|---|---|
hibernate.current_session_context_class
| Supply a custom strategy for the scoping of the "current" Session. See 「コンテキスト上のセッション」 for more information about the built-in strategies. 例 |
hibernate.query.factory_class
| HQL パーサーの実装を選択します。 例 |
hibernate.query.substitutions
| HQL と SQL のトークンをマッピングします。 (例えば、トークンは関数やリテラル名です)。 例 |
hibernate.hbm2ddl.auto
| SessionFactory を生成したときに、自動的にスキーマ DDL を有効にしデータベースに出力します。 create-drop の場合、 SessionFactory をクローズしたときに、データベーススキーマをドロップします。 例 |
hibernate.bytecode.use_reflection_optimizer
|
Enables the use of bytecode manipulation instead of runtime reflection. This is a System-level property and cannot be set in 例 |
hibernate.bytecode.provider
| Both javassist or cglib can be used as byte manipulation engines; the default is e.g. |
hibernate.dialect プロパティには、使用するデータベースの正しい org.hibernate.dialect.Dialect のサブクラスを、必ず指定すべきです。しかし方言を指定すれば、 Hibernate は上述したプロパティのいくつかについて、より適切なデフォルト値を使います。そうすれば、それらを手作業で設定する手間が省けます。
表3.8 Hibernate SQL Dialects (hibernate.dialect)
| RDBMS | Dialect |
|---|---|
| DB2 | org.hibernate.dialect.DB2Dialect |
| DB2 AS/400 | org.hibernate.dialect.DB2400Dialect |
| DB2 OS390 | org.hibernate.dialect.DB2390Dialect |
| PostgreSQL | org.hibernate.dialect.PostgreSQLDialect |
| MySQL | org.hibernate.dialect.MySQLDialect |
| MySQL with InnoDB | org.hibernate.dialect.MySQLInnoDBDialect |
| MySQL with MyISAM | org.hibernate.dialect.MySQLMyISAMDialect |
| Oracle (いずれのバージョンでも) | org.hibernate.dialect.OracleDialect |
| Oracle 9i | org.hibernate.dialect.Oracle9iDialect |
| Oracle 10g | org.hibernate.dialect.Oracle10gDialect |
| Sybase | org.hibernate.dialect.SybaseDialect |
| Sybase Anywhere | org.hibernate.dialect.SybaseAnywhereDialect |
| Microsoft SQL Server | org.hibernate.dialect.SQLServerDialect |
| SAP DB | org.hibernate.dialect.SAPDBDialect |
| Informix | org.hibernate.dialect.InformixDialect |
| HypersonicSQL | org.hibernate.dialect.HSQLDialect |
| Ingres | org.hibernate.dialect.IngresDialect |
| Progress | org.hibernate.dialect.ProgressDialect |
| Mckoi SQL | org.hibernate.dialect.MckoiDialect |
| Interbase | org.hibernate.dialect.InterbaseDialect |
| Pointbase | org.hibernate.dialect.PointbaseDialect |
| FrontBase | org.hibernate.dialect.FrontbaseDialect |
| Firebird | org.hibernate.dialect.FirebirdDialect |
もしデータベースが ANSI か、 Oracle か Sybase スタイルの外部結合をサポートしている場合、 outer join fetching は、データベースの SQL 発行回数を節約しパフォーマンスを良くします(データベース内でより多くの処理コストが発生します)。外部結合フェッチは、多対一、一対多、多対多、一対一のオブジェクト関連でグループオブジェクトを1つの SQL で SELECT します。
hibernate.max_fetch_depth プロパティの値を 0 にすると外部結合フェッチを すべて 無効にすることになります。 1 やそれ以上の値を設定すると、外部結合フェッチが有効になり、一対一と多対一関連が fetch="join" としてマッピングされます。
See 「フェッチ戦略」 for more information.
Oracle は JDBC ドライバとの間でやりとりされる byte 配列のサイズを制限します。 binary や serializable 型の大きなインスタンスを使いたければ、 hibernate.jdbc.use_streams_for_binary を有効にしてください。 ただし これはシステムレベルの設定だけです 。
The properties prefixed by hibernate.cache allow you to use a process or cluster scoped second-level cache system with Hibernate. See the 「第2レベルキャッシュ」 for more information.
hibernate.query.substitutions を使うことで、新しい Hibernate クエリトークンを定義できます。例:
hibernate.query.substitutions true=1, false=0
これはトークン true と false を、生成される SQL において整数リテラルに翻訳します。
hibernate.query.substitutions toLowercase=LOWER
これは SQL の LOWER 関数の名前の付け替えを可能にします。
Hibernate utilizes Simple Logging Facade for Java (SLF4J) in order to log various system events. SLF4J can direct your logging output to several logging frameworks (NOP, Simple, log4j version 1.2, JDK 1.4 logging, JCL or logback) depending on your chosen binding. In order to setup logging you will need slf4j-api.jar in your classpath together with the jar file for your preferred binding - slf4j-log4j12.jar in the case of Log4J. See the SLF4J documentation for more detail. To use Log4j you will also need to place a log4j.properties file in your classpath. An example properties file is distributed with Hibernate in the src/ directory.
Hibernate のログメッセージに慣れることを強くおすすめします。 Hibernate のログは読みやすく、できる限り詳細になるように努力されています。これは必須のトラブルシューティングデバイスです。以下に重要なログのカテゴリを示します:
表3.9 Hibernate ログカテゴリ
| カテゴリ | 機能 |
|---|---|
org.hibernate.SQL | 実行したすべての SQL(DDL)ステートメントをロギングします。 |
org.hibernate.type | すべての JDBC パラメータをロギングします。 |
org.hibernate.tool.hbm2ddl | 実行したすべての SQL(DDL)ステートメントをロギングします。 |
org.hibernate.pretty | session に関連するすべてのエンティティ(最大20)のフラッシュ時間をロギングします。 |
org.hibernate.cache | すべてのニ次キャッシュの動作をロギングします。 |
org.hibernate.transaction | トランザクションに関連する動作をロギングします。 |
org.hibernate.jdbc | JDBC リソース取得をロギングします。 |
org.hibernate.hql.ast.AST | HQL と SQL の AST のクエリパースをロギングします。 |
org.hibernate.secure | すべての JAAS 分析をロギングします。 |
org.hibernate | すべてをロギングします。(情報が大量になりますが、トラブルシューティングには便利です) |
Hibernate でアプリケーションを作成するときは、 org.hibernate.SQL カテゴリの debug を常に有効にしておいたほうが良いでしょう。代替方法として、 hibernate.show_sql プロパティを有効にする方法があります。
インターフェース net.sf.hibernate.cfg.NamingStrategy を使うとデータベースオブジェクトとスキーマ要素のための「命名標準」を指定できます。
Java の識別子からデータベースの識別子を自動生成するためのルールや、マッピングファイルで与えた「論理的な」カラムとテーブル名から「物理的な」テーブルとカラム名を生成するためのルールを用意することができます。この機能は繰り返しの雑音(例えば TBL_ プリフィックス)を取り除き、マッピングドキュメントの冗長さを減らすことに役立ちます。 Hibernate が使うデフォルトの戦略はかなり最小限に近いものです。
マッピングを追加する前に Configuration.setNamingStrategy() を呼ぶことで以下のように異なる戦略を指定することができます:
SessionFactory sf = new Configuration()
.setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
.addFile("Item.hbm.xml")
.addFile("Bid.hbm.xml")
.buildSessionFactory();
org.hibernate.cfg.ImprovedNamingStrategy は組み込みの戦略です。これはいくつかのアプリケーションにとって有用な開始点となるかもしれません。
もう1つの方法は hibernate.cfg.xml という名前のファイルで十分な設定を指定する方法です。このファイルは hibernate.properties ファイルの代わりとなります。もし両方のファイルがあれば、プロパティが置き換えられます。
XML 設定ファイルは初期設定で CLASSPATH の root に配置してください。これが例です:
<?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>
<!-- a SessionFactory instance listed as /jndi/name -->
<session-factory
name="java:hibernate/SessionFactory">
<!-- properties -->
<property name="connection.datasource"
>java:/comp/env/jdbc/MyDB</property>
<property name="dialect"
>org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql"
>false</property>
<property name="transaction.factory_class">
org.hibernate.transaction.JTATransactionFactory
</property>
<property name="jta.UserTransaction"
>java:comp/UserTransaction</property>
<!-- mapping files -->
<mapping resource="org/hibernate/auction/Item.hbm.xml"/>
<mapping resource="org/hibernate/auction/Bid.hbm.xml"/>
<!-- cache settings -->
<class-cache class="org.hibernate.auction.Item" usage="read-write"/>
<class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
<collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/>
</session-factory>
</hibernate-configuration
>
見てのとおり、この方法の優位性は設定のためのマッピングファイル名を外出しにできることです。 Hibernate キャッシュをチューニングしなければならないのであれば、 hibernate.cfg.xml はより便利です。 hibernate.properties と hibernate.cfg.xml の どちらかを使えることを覚えておいてください。2つは同じもので、違うところといえば XML 構文を使うことの利点だけです。
XML 設定を使うことで、 Hibernate は以下のようにシンプルになります。
SessionFactory sf = new Configuration().configure().buildSessionFactory();
違う XML 設定ファイルを使うこともできます。
SessionFactory sf = new Configuration()
.configure("catdb.cfg.xml")
.buildSessionFactory();
Hibernate は J2EE 構造と統合するポイントをサポートしています:
コンテナ管理データソース: Hibernate は JNDI が提供し、コンテナが管理する JDBC コネクションを使用できます。通常、 JTA 準拠の TransactionManager と ResourceManager がトランザクション管理 (CMT)、特に様々なデータソースにまたがる分散トランザクションを扱います。当然プログラムでトランザクション境界を指定できます (BMT)。あるいは、記述したコードのポータビリティを保つために、オプションの Hibernate の Transaction API を使いたくなるかもしれません。
自動 JNDI バインディング: Hibernate は JNDI が立ち上がった後に SessionFactory を生成します。
JTA セッションバインディング: Hibernate Session は自動的に JTA トランザクションのスコープにバインドされます。単純に SessionFactory を JNDI から lookup して、現在の Session を取得します。 JTA トランザクションが完了したときに、 Hibernateが Session をフラッシュし、クローズします。トランザクション境界は、宣言 (CMT) することも、プログラム (BMT/UserTransaction) することも可能です。
JMX デプロイメント: もし JMX が使用可能なアプリケーションサーバー(例えば JBoss AS) がある場合、 Hibernate を MBean としてデプロイすることを選べます。これは Configuration から SessionFactory を生成するコードを無くすことができます。コンテナは HibernateService を起動し、サービスの依存を理想的に管理します(データソースは Hibernate やその他が起動する前に使用できるようにしなければなりません)。
環境に依存しますが、もしアプリケーションサーバーが "connection containment" の例外を出す場合、設定のオプション hibernate.connection.aggressive_release を true にしてください。
Hibernate Session API は、アーキテクチャ内のシステムの管轄であるあらゆるトランザクションに依存しません。もしコネクションプールの JDBC を直接使いたい場合、 JDBC API から トランザクションを呼ぶことができます。もし、 J2EE アプリケーションサーバーで動作させるなら、 Bean 管理トランザクションを使い、必要に応じて UserTransaction を JTA API から呼ぶことになるでしょう。
2つ(それ以上)の環境で互換性のあるコードを維持するために、オプションとして根本的なシステムをラッピングする Hibernate Transaction API を推奨します。 Hibernate 設定プロパティの hibernate.transaction.factory_class を設定することで、ある特定の Transaction クラスのインスタンスを持つことができます。
3つの基本的な(既にある)選択を挙げます:
org.hibernate.transaction.JDBCTransactionFactoryデータベース (JDBC) トランザクションに委譲します(デフォルト)
org.hibernate.transaction.JTATransactionFactoryもし、このコンテキスト(例えば、 EJB セッション Bean メソッド)で進行中のトランザクションが存在する場合、コンテナ管理トランザクションに委譲します。そうでない場合は、新しいトランザクションが開始されており、 Bean 管理トランザクションが使われます。
org.hibernate.transaction.CMTTransactionFactoryコンテナ管理 JTA トランザクションに委譲します
自分自身のトランザクション戦略(例えば、 CORBA トランザクションサービス)を定義することもできます。
Hibernate のいくつかの機能(例えば、二次キャッシュ、 JTA によるコンテキストセッション等)は管理された環境の中の JTA TransactionManager へのアクセスを要求します。 J2EE がひとつのメカニズムに規格化されていないので、アプリケーションサーバーにおいて、 Hibernateが TransactionManager のリファレンスを取得する方法を明確にする必要があります。
表3.10 JTA トランザクションマネージャ
| Transaction Factory | Application Server |
|---|---|
org.hibernate.transaction.JBossTransactionManagerLookup | JBoss |
org.hibernate.transaction.WeblogicTransactionManagerLookup | Weblogic |
org.hibernate.transaction.WebSphereTransactionManagerLookup | WebSphere |
org.hibernate.transaction.WebSphereExtendedJTATransactionLookup | WebSphere 6 |
org.hibernate.transaction.OrionTransactionManagerLookup | Orion |
org.hibernate.transaction.ResinTransactionManagerLookup | Resin |
org.hibernate.transaction.JOTMTransactionManagerLookup | JOTM |
org.hibernate.transaction.JOnASTransactionManagerLookup | JOnAS |
org.hibernate.transaction.JRun4TransactionManagerLookup | JRun4 |
org.hibernate.transaction.BESTransactionManagerLookup | Borland ES |
JNDI に登録した Hibernate SessionFactory はファクトリのルックアップと新しい Session の作成を簡易化します。これは JNDI に登録された Datasource には関連せず、両方とも単に同じ登録を使うことに注意してください。
もし SessionFactory を JNDI ネームスペースに登録したい場合、特別な名前(例えば、 java:hibernate/SessionFactory )を hibernate.session_factory_name プロパティに使ってください。もしこのプロパティを省略した場合、 SessionFactory は JNDI に登録されません。(これは Tomcat のようなデフォルト実装で JNDI が読み取り専用の環境の場合は特に便利です。)
SessionFactory を JNDI に登録するとき、 Hibernate は hibernate.jndi.url の値を使用し、hibernate.jndi.class をイニシャルコンテキストとして具体化します。もし何も設定しない場合は、デフォルトの InitialContext を使用します。
cfg.buildSessionFactory() をコール後 Hibernate は自動的に SessionFactory を JNDI に配置します。 HibernateService と一緒に JMX デプロイメントを使わない限り、これはこの呼び出しをアプリケーション内の何らかのスタートアップコード(もしくはユーティリティクラス) に配置しなければならないことを意味します(後で議論します)。
もし JNDI SessionFactory を使う場合、 EJB や他のクラスは JNDI ルックアップを使って SessionFactory を取得します。
管理された環境では SessionFactory を JNDI にバインドし、そうでなければ static シングルトンを使うことを推奨します。こういった詳細からアプリケーションコードを保護するために、 HibernateUtil.getSessionFactory() のようなヘルパークラスの中に、 SessionFactory をルックアップするコードを隠すことを推奨します。このようなヘルパークラスは Hibernate を開始する便利な手段でもあります。 - 1章を参照してください。
The easiest way to handle Sessions and transactions is Hibernate's automatic "current" Session management. For a discussion of contextual sessions see 「コンテキスト上のセッション」. Using the "jta" session context, if there is no Hibernate Session associated with the current JTA transaction, one will be started and associated with that JTA transaction the first time you call sessionFactory.getCurrentSession(). The Sessions retrieved via getCurrentSession() in the "jta" context are set to automatically flush before the transaction completes, close after the transaction completes, and aggressively release JDBC connections after each statement. This allows the Sessions to be managed by the life cycle of the JTA transaction to which it is associated, keeping user code clean of such management concerns. Your code can either use JTA programmatically through UserTransaction, or (recommended for portable code) use the Hibernate Transaction API to set transaction boundaries. If you run in an EJB container, declarative transaction demarcation with CMT is preferred.
SessionFactory を JNDI から取得するためには cfg.buildSessionFactory() 行をどこかで実行していなければなりません。あなたはこれを、 static 初期化ブロック内( HibernateUtil のような)か managed service として Hibernate をデプロイするか、どちらかで実行できます。
JBoss AS のような JMX の機能でアプリケーションサーバーにデプロイするために org.hibernate.jmx.HibernateService を使って、配置します。実際のデプロイメントと設定はベンダー特有です。ここで例として JBoss 4.0.x 用の jboss-service.xml を示します。
<?xml version="1.0"?>
<server>
<mbean code="org.hibernate.jmx.HibernateService"
name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
<!-- Required services -->
<depends
>jboss.jca:service=RARDeployer</depends>
<depends
>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>
<!-- Bind the Hibernate service to JNDI -->
<attribute name="JndiName"
>java:/hibernate/SessionFactory</attribute>
<!-- Datasource settings -->
<attribute name="Datasource"
>java:HsqlDS</attribute>
<attribute name="Dialect"
>org.hibernate.dialect.HSQLDialect</attribute>
<!-- Transaction integration -->
<attribute name="TransactionStrategy">
org.hibernate.transaction.JTATransactionFactory</attribute>
<attribute name="TransactionManagerLookupStrategy">
org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
<attribute name="FlushBeforeCompletionEnabled"
>true</attribute>
<attribute name="AutoCloseSessionEnabled"
>true</attribute>
<!-- Fetching options -->
<attribute name="MaximumFetchDepth"
>5</attribute>
<!-- Second-level caching -->
<attribute name="SecondLevelCacheEnabled"
>true</attribute>
<attribute name="CacheProviderClass"
>org.hibernate.cache.EhCacheProvider</attribute>
<attribute name="QueryCacheEnabled"
>true</attribute>
<!-- Logging -->
<attribute name="ShowSqlEnabled"
>true</attribute>
<!-- Mapping files -->
<attribute name="MapResources"
>auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>
</mbean>
</server
>
このファイルは META-INF ディレクトリに配置され、 JAR ファイルを拡張した .sar (service archive) でパッケージ化されます。同様に Hibernate パッケージも必要です。また、 Hibernate はサードパーティのライブラリも要求します。コンパイルした永続化クラスとそのマッピングファイルも同様にアーカイブ(.sarファイル)に入れます。エンタープライズ Bean (通常はセッション Bean )は自身の JAR ファイルを保持しますが、1回で(ホット)デプロイ可能なユニットのためにメインサービスアーカイブとしてこの EJB JAR ファイルを含めることができます。 JBoss AS のドキュメントに JXM サービスと EJB デプロイメントのより多くの情報があります。
永続クラスはビジネス上の問題のエンティティ(例えば、 E コマースアプリケーションの顧客や注文) を実装するアプリケーションのクラスです。永続クラスのすべてのインスタンスが永続状態であると見なされるわけではありません。インスタンスは逆に一時的(transient)であったり、分離状態(detached)であったりするかもしれません。
Plain Old Java Object (POJO)プログラミングモデルとしても知られるいくつかの単純なルールに従うなら、 Hibernate は最もよく働きます。しかしこれらのルールは難しいものではありません。実際 Hibernate3 は永続オブジェクトの性質にほとんど何の前提も置いていません。ドメインモデルは他の方法で表現することもできます。例えば Map インスタンスのツリーを使う方法があります。
Most Java applications require a persistent class representing felines. For example:
package eg;
import java.util.Set;
import java.util.Date;
public class Cat {
private Long id; // identifier
private Date birthdate;
private Color color;
private char sex;
private float weight;
private int litterId;
private Cat mother;
private Set kittens = new HashSet();
private void setId(Long id) {
this.id=id;
}
public Long getId() {
return id;
}
void setBirthdate(Date date) {
birthdate = date;
}
public Date getBirthdate() {
return birthdate;
}
void setWeight(float weight) {
this.weight = weight;
}
public float getWeight() {
return weight;
}
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
void setSex(char sex) {
this.sex=sex;
}
public char getSex() {
return sex;
}
void setLitterId(int id) {
this.litterId = id;
}
public int getLitterId() {
return litterId;
}
void setMother(Cat mother) {
this.mother = mother;
}
public Cat getMother() {
return mother;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
public Set getKittens() {
return kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kitten.setMother(this);
kitten.setLitterId( kittens.size() );
kittens.add(kitten);
}
}
The four main rules of persistent classes are explored in more detail in the following sections.
Cat には引数のないコンストラクタがあります。 Hibernate が Constructor.newInstance() を使って永続クラスのインスタンス化を行えるように、すべての永続クラスにはデフォルトコンストラクタ (public でなくても構いません) がなければなりません。 Hibernate の実行時プロキシ生成のために、少なくとも package の可視性を持つデフォルトコンストラクタを強くお勧めします。
Cat has a property called id. This property maps to the primary key column of a database table. The property might have been called anything, and its type might have been any primitive type, any primitive "wrapper" type, java.lang.String or java.util.Date. If your legacy database table has composite keys, you can use a user-defined class with properties of these types (see the section on composite identifiers later in the chapter.)
識別子プロパティは厳密にはオプションです。これを省略して、 Hibernate に内部的にオブジェクトの識別子を追跡させることは可能です。しかしお勧めはしません。
実際に、識別子プロパティを宣言するクラスだけが利用可能な機能がいくつかあります:
Transitive reattachment for detached objects (cascade update or cascade merge) - see 「連鎖的な永続化」
Session.saveOrUpdate()
Session.merge()
永続クラスには、一貫した名前の識別子プロパティを定義することをお勧めします。さらに null 値を取れる(つまりプリミティブではない)型を使った方がよいでしょう。
Hibernate の中心的な特徴である プロキシ は、永続クラスが final でないこと、またはメソッドを全部 public で宣言しているインターフェースが実装されているかに依存しています。
Hibernate でインターフェースを実装していない final クラスを永続化することはできますが、遅延関連フェッチに対してプロキシを使うことはできなくなります。これはパフォーマンスチューニングへの選択肢を狭めることになります。
final ではないクラスで public final メソッドを定義することも避けるべきです。 public final メソッドを持つクラスを使いたければ、 lazy="false" と設定して明示的にプロキシを無効にしなければなりません。
Cat declares accessor methods for all its persistent fields. Many other ORM tools directly persist instance variables. It is better to provide an indirection between the relational schema and internal data structures of the class. By default, Hibernate persists JavaBeans style properties and recognizes method names of the form getFoo, isFoo and setFoo. If required, you can switch to direct field access for particular properties.
プロパティは public で宣言する必要は ありません 。 Hibernate はデフォルトで、 protected もしくは private の get / set のペアを持つプロパティを永続化することができます。
サブクラスも1番目と2番目のルールを守らなければなりません。サブクラスはスーパークラス Cat から識別子プロパティを継承します。
package eg;
public class DomesticCat extends Cat {
private String name;
public String getName() {
return name;
}
protected void setName(String name) {
this.name=name;
}
}
以下の条件の場合、 equals() と hashCode() メソッドをオーバーライドしなければなりません、
永続クラスのインスタンスを Set に置く場合。 (これは多値の関連を表現するおすすめの方法です) そして同時に
分離インスタンスをセッションへ再追加する場合。
Hibernate は、永続 ID (データベースの行)と、特定のセッションスコープ内に限定ですが Java ID とが等価であることを保証します。ですから異なるセッションで検索したインスタンスを組み合わせる場合、 Set に意味のあるセマンティクスを持たせようと思っているならすぐに equals() と hashCode() を実装しなければなりません。
最も明白な方法は、両方のオブジェクトの識別子の値の比較によって equals()と hashCode() を実装する方法です。値が同じなら、両者はデータベースの同じ行でなければならないため等しくなります。 (両者が Set に追加されても、 Set には1個の要素しかないことになります) 残念なことに、生成された識別子にはこのアプローチを使うことができません。 Hibernate は永続化されたオブジェクトへ識別子の値を代入するだけであり、新しく作成されたインスタンスはどのような識別子の値も持っていません。さらに、インスタンスがセーブされておらず、現在 Set の中にあれば、セーブするとオブジェクトへ識別子の値を代入することになります。もし equals() と hashCode() が識別子の値に基づいているなら、ハッシュコードが変更されると Set の規約が破られます。この問題についての完全な議論は、 Hibernate のウェブサイトを見てください。これは Hibernate の問題ではなく、オブジェクトの同一性と等価性についての、通常の Java のセマンティクスであることに注意してください。
ビジネスキーの等価性 を使って、 equals() と hashCode() を実装することをお勧めします。ビジネスキーの等価性とは、 equals() メソッドが、ビジネスキー、つまり現実の世界においてインスタンスを特定するキー(自然 候補キー) を形成するプロパティだけを比較することを意味します。
public class Cat {
...
public boolean equals(Object other) {
if (this == other) return true;
if ( !(other instanceof Cat) ) return false;
final Cat cat = (Cat) other;
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
if ( !cat.getMother().equals( getMother() ) ) return false;
return true;
}
public int hashCode() {
int result;
result = getMother().hashCode();
result = 29 * result + getLitterId();
return result;
}
}
A business key does not have to be as solid as a database primary key candidate (see 「オブジェクト識別子を考える」). Immutable or unique properties are usually good candidates for a business key.
The following features are currently considered experimental and may change in the near future.
永続エンティティは、必ずしも実行時に POJO クラスや JavaBean オブジェクトで表現する必要はありません。 Hibernate は(実行時に Map の Map を使う)動的モデルと、 DOM4J ツリーとしてのエンティティの表現もサポートします。このアプローチを使うと永続クラスを書かず、マッピングファイルだけを書くことになります。
By default, Hibernate works in normal POJO mode. You can set a default entity representation mode for a particular SessionFactory using the default_entity_mode configuration option (see 表3.3「Hibernate 設定プロパティ」).
以下の例では Map を使った表現を紹介します。まずマッピングファイルで、クラス名の代わりに(またはそれに加えて) entity-name を定義しなければなりません:
<hibernate-mapping>
<class entity-name="Customer">
<id name="id"
type="long"
column="ID">
<generator class="sequence"/>
</id>
<property name="name"
column="NAME"
type="string"/>
<property name="address"
column="ADDRESS"
type="string"/>
<many-to-one name="organization"
column="ORGANIZATION_ID"
class="Organization"/>
<bag name="orders"
inverse="true"
lazy="false"
cascade="all">
<key column="CUSTOMER_ID"/>
<one-to-many class="Order"/>
</bag>
</class>
</hibernate-mapping
>
関連がターゲットのクラス名を使って定義していたとしても、関連のターゲット型も POJO ではなく動的なエンティティでも構わないことに注意してください。
SessionFactory に対してデフォルトのエンティティモードを dynamic-map に設定した後、実行時に Map の Map を使うことができます:
Session s = openSession();
Transaction tx = s.beginTransaction();
// Create a customer
Map david = new HashMap();
david.put("name", "David");
// Create an organization
Map foobar = new HashMap();
foobar.put("name", "Foobar Inc.");
// Link both
david.put("organization", foobar);
// Save both
s.save("Customer", david);
s.save("Organization", foobar);
tx.commit();
s.close();
動的なマッピングの利点は、エンティティクラスの実装を必要としないため、プロトタイピングに要するターンアラウンドタイムが早いということです。しかしコンパイル時の型チェックがないので、実行時に非常に多くの例外処理を扱わなければならないでしょう。 Hibernate マッピングのおかげで、データベーススキーマは容易に正規化でき、健全になり、後で適切なドメインモデルの実装を追加することが可能になります。
エンティティ表現モードは Session ごとに設定することも可能です。
Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
// Create a customer
Map david = new HashMap();
david.put("name", "David");
dynamicSession.save("Customer", david);
...
dynamicSession.flush();
dynamicSession.close()
...
// Continue on pojoSession
EntityMode を使った getSession() の呼び出しは SessionFactory ではなく Session APIにあることに注意してください。その方法では、新しい Session は、ベースとなる JDBC コネクション、トランザクション、その他のコンテキスト情報を共有します。これは2番目の Session では flush() と close() を呼ぶ必要がないということ、そのためトランザクションとコネクションの管理を1番目の作業単位(Unit of Work)に任せることができるということです。
More information about the XML representation capabilities can be found in 19章XML マッピング.
org.hibernate.tuple.Tuplizer とそのサブインターフェースは、表現の org.hibernate.EntityMode を利用して、データ断片のある表現の管理に責任を持ちます。与えられたデータ断片をデータ構造として考えるなら、 Tuplizer はそのようなデータ構造をどのように作成するかを知り、そのようなデータ構造からどのように値を抽出し、注入するかを知っています。例えば POJO エンティティモードでは、対応する Tuplizer はコンストラクタを通して、 POJO をどのように作成するか、定義されたプロパティアクセサを使い、 POJO プロパティにどのようにアクセスするかを知ります。
Tuplizer には二つのハイレベルの型があります。それらは、org.hibernate.tuple.entity.EntityTuplizer と org.hibernate.tuple.component.ComponentTuplizer インターフェースで表現されます。 EntityTuplizer は上で述べたようなエンティティに関する契約の管理に責任を持ちます。一方、 ComponentTuplizer はコンポーネントに関する契約の管理に責任を持ちます。
ユーザーは独自の Tuplizer に差し替えることも可能です。おそらく dynamic-map entity-mode の際に java.util.HashMap を使うのではなく、 java.util.Map の実装が必要でしょう。もしくは、おそらくデフォルトのものではなく、別のプロキシ生成戦略の定義が必要でしょう。両者とも、カスタムの Tuplizer 実装を定義することで達成されます。 Tuplizer の定義は、管理しようとするエンティティやコンポーネントのマッピングに結び付けられます。顧客エンティティの例は以下になります:
<hibernate-mapping>
<class entity-name="Customer">
<!--
Override the dynamic-map entity-mode
tuplizer for the customer entity
-->
<tuplizer entity-mode="dynamic-map"
class="CustomMapTuplizerImpl"/>
<id name="id" type="long" column="ID">
<generator class="sequence"/>
</id>
<!-- other properties -->
...
</class>
</hibernate-mapping>
public class CustomMapTuplizerImpl
extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer {
// override the buildInstantiator() method to plug in our custom map...
protected final Instantiator buildInstantiator(
org.hibernate.mapping.PersistentClass mappingInfo) {
return new CustomMapInstantiator( mappingInfo );
}
private static final class CustomMapInstantiator
extends org.hibernate.tuple.DynamicMapInstantitor {
// override the generateMap() method to return our custom map...
protected final Map generateMap() {
return new CustomMap();
}
}
}
The org.hibernate.EntityNameResolver interface is a contract for resolving the entity name of a given entity instance. The interface defines a single method resolveEntityName which is passed the entity instance and is expected to return the appropriate entity name (null is allowed and would indicate that the resolver does not know how to resolve the entity name of the given entity instance). Generally speaking, an org.hibernate.EntityNameResolver is going to be most useful in the case of dynamic models. One example might be using proxied interfaces as your domain model. The hibernate test suite has an example of this exact style of usage under the org.hibernate.test.dynamicentity.tuplizer2. Here is some of the code from that package for illustration.
/**
* A very trivial JDK Proxy InvocationHandler implementation where we proxy an interface as
* the domain model and simply store persistent state in an internal Map. This is an extremely
* trivial example meant only for illustration.
*/
public final class DataProxyHandler implements InvocationHandler {
private String entityName;
private HashMap data = new HashMap();
public DataProxyHandler(String entityName, Serializable id) {
this.entityName = entityName;
data.put( "Id", id );
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if ( methodName.startsWith( "set" ) ) {
String propertyName = methodName.substring( 3 );
data.put( propertyName, args[0] );
}
else if ( methodName.startsWith( "get" ) ) {
String propertyName = methodName.substring( 3 );
return data.get( propertyName );
}
else if ( "toString".equals( methodName ) ) {
return entityName + "#" + data.get( "Id" );
}
else if ( "hashCode".equals( methodName ) ) {
return new Integer( this.hashCode() );
}
return null;
}
public String getEntityName() {
return entityName;
}
public HashMap getData() {
return data;
}
}
/**
*
*/
public class ProxyHelper {
public static String extractEntityName(Object object) {
// Our custom java.lang.reflect.Proxy instances actually bundle
// their appropriate entity name, so we simply extract it from there
// if this represents one of our proxies; otherwise, we return null
if ( Proxy.isProxyClass( object.getClass() ) ) {
InvocationHandler handler = Proxy.getInvocationHandler( object );
if ( DataProxyHandler.class.isAssignableFrom( handler.getClass() ) ) {
DataProxyHandler myHandler = ( DataProxyHandler ) handler;
return myHandler.getEntityName();
}
}
return null;
}
// various other utility methods ....
}
/**
* The EntityNameResolver implementation.
* IMPL NOTE : An EntityNameResolver really defines a strategy for how entity names should be
* resolved. Since this particular impl can handle resolution for all of our entities we want to
* take advantage of the fact that SessionFactoryImpl keeps these in a Set so that we only ever
* have one instance registered. Why? Well, when it comes time to resolve an entity name,
* Hibernate must iterate over all the registered resolvers. So keeping that number down
* helps that process be as speedy as possible. Hence the equals and hashCode impls
*/
public class MyEntityNameResolver implements EntityNameResolver {
public static final MyEntityNameResolver INSTANCE = new MyEntityNameResolver();
public String resolveEntityName(Object entity) {
return ProxyHelper.extractEntityName( entity );
}
public boolean equals(Object obj) {
return getClass().equals( obj.getClass() );
}
public int hashCode() {
return getClass().hashCode();
}
}
public class MyEntityTuplizer extends PojoEntityTuplizer {
public MyEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
super( entityMetamodel, mappedEntity );
}
public EntityNameResolver[] getEntityNameResolvers() {
return new EntityNameResolver[] { MyEntityNameResolver.INSTANCE };
}
public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) {
String entityName = ProxyHelper.extractEntityName( entityInstance );
if ( entityName == null ) {
entityName = super.determineConcreteSubclassEntityName( entityInstance, factory );
}
return entityName;
}
...
}
In order to register an org.hibernate.EntityNameResolver users must either:
Implement a custom Tuplizer, implementing the getEntityNameResolvers method.
Register it with the org.hibernate.impl.SessionFactoryImpl (which is the implementation class for org.hibernate.SessionFactory) using the registerEntityNameResolver method.
オブジェクト/リレーショナルマッピングは通常 XML ドキュメントで定義します。マッピングドキュメントは、読みやすく手作業で編集しやすいようにデザインされています。マッピング言語は Java 中心、つまりテーブル定義ではなく永続クラスの定義に基づいて構築されています。
多くの Hibernate ユーザーは XML マッピングの記述を手作業で行いますが、 XDoclet, Middlegen, AndroMDA というようなマッピングドキュメントを生成するツールがいくつか存在することを覚えておいてください。
サンプルのマッピングから始めましょう:
<?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 package="eg">
<class name="Cat"
table="cats"
discriminator-value="C">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="subclass"
type="character"/>
<property name="weight"/>
<property name="birthdate"
type="date"
not-null="true"
update="false"/>
<property name="color"
type="eg.types.ColorUserType"
not-null="true"
update="false"/>
<property name="sex"
not-null="true"
update="false"/>
<property name="litterId"
column="litterId"
update="false"/>
<many-to-one name="mother"
column="mother_id"
update="false"/>
<set name="kittens"
inverse="true"
order-by="litter_id">
<key column="mother_id"/>
<one-to-many class="Cat"/>
</set>
<subclass name="DomesticCat"
discriminator-value="D">
<property name="name"
type="string"/>
</subclass>
</class>
<class name="Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping
>
マッピングドキュメントの内容を説明します。ただし、ここでは Hibernate が実行時に使うドキュメント要素と属性についてのみ説明します。マッピングドキュメントは、いくつかのオプション属性と要素を含んでいます(例えば not-null 属性)。それらはスキーマエクスポートツールが出力するデータベーススキーマに影響を与えるものです。
XML マッピングでは、お見せしたようなドキュメント型を必ず定義すべきです。実際の DTD は、上記の URL の hibernate-x.x.x/src/org/hibernate ディレクトリ、または hibernate.jar 内にあります。 Hibernate は常に、そのクラスパス内で DTD を探し始めます。インターネットにある DTD ファイルを探そうとしたなら、クラスパスの内容を見て、 DTD 宣言を確認してください。
前述したように、 Hibernate はまずクラスパス内で DTD を解決しようとします。 org.xml.sax.EntityResolver のカスタム実装を XML ファイルを読み込むための SAXReader に登録することによって、 DTD を解決します。このカスタムの EntityResolver は2つの異なるシステム ID 名前空間を認識します。
hibernate namespace は、リゾルバが http://hibernate.sourceforge.net/ で始まるシステム ID に到達したときに認識されます。そしてリゾルバは、 Hibernate のクラスをロードしたクラスローダを用いて、これらのエンティティを解決しようとします。
user namespace は、リゾルバが URL プロトコルの classpath:// を使ったシステム ID に到達したときに、認識されます。そしてリゾルバは、 (1) カレントスレッドのコンテキストクラスローダー、または (2) Hibernate のクラスをロードしたクラスローダを使って、これらのエンティティを解決しようとします。
下記は、ユーザー名前空間を使った例です:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC '-//Hibernate/Hibernate Mapping DTD 3.0//EN' 'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd' [
<!ENTITY version "3.5.6-Final">
<!ENTITY today "September 15, 2010">
<!ENTITY types SYSTEM "classpath://your/domain/types.xml">
]>
<hibernate-mapping package="your.domain">
<class name="MyEntity">
<id name="id" type="my-custom-id-type">
...
</id>
<class>
&types;
</hibernate-mapping>
Where types.xml is a resource in the your.domain package and contains a custom typedef.
この要素にはいくつかオプション属性があります。 schema 属性と catalog 属性は、このマッピングが参照するテーブルが、この属性によって指定されたスキーマと(または)カタログに属することを指定します。この属性が指定されると、テーブル名は与えられたスキーマ名とカタログ名で修飾されます。これらの属性が指定されていなければ、テーブル名は修飾されません。 default-cascade 属性は、 cascade 属性を指定していないプロパティやコレクションに、どのカスケードスタイルを割り当てるかを指定します。 auto-import 属性は、クエリ言語内で修飾されていないクラス名を、デフォルトで使えるようにします。
<hibernate-mapping
schem
a="schemaName"
catal
og="catalogName"
defau
lt-cascade="cascade_style"
defau
lt-access="field|property|ClassName"
defau
lt-lazy="true|false"
auto-
import="true|false"
packa
ge="package.name"
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(修飾されていない)同じ名前の永続クラスが2つあるなら、 auto-import="false" を設定すべきです。2つのクラスに"インポートされた"同じ名前を割り当てようとすると、 Hibernate は例外を送出します。
hibernate-mapping 要素は、最初の例で示したようにいくつかの永続 <class> マッピングをネストできます。しかし、1つのマッピングファイルではただひとつの永続クラス(またはひとつのクラス階層)にマッピングするようにし、さらに永続スーパークラスの後で指定するべきでしょう(いくつかのツールはこのようなマッピングファイルを想定しています)。例えば次のようになります。: Cat.hbm.xml , Dog.hbm.xml , または継承を使うなら Animal.hbm.xml 。
class 要素を使って、永続クラスを宣言できます:
<class
name="
ClassName"
table=
"tableName"
discri
minator-value="discriminator_value"
mutabl
e="true|false"
schema
="owner"
catalo
g="catalog"
proxy=
"ProxyInterface"
dynami
c-update="true|false"
dynami
c-insert="true|false"
select
-before-update="true|false"
polymo
rphism="implicit|explicit"
where=
"arbitrary sql where condition"
persis
ter="PersisterClass"
batch-
size="N"
optimi
stic-lock="none|version|dirty|all"
lazy="(16)true|false"
entity(17)-name="EntityName"
check=(18)"arbitrary sql check condition"
rowid=(19)"rowid"
subsel(20)ect="SQL expression"
abstra(21)ct="true|false"
node="element-name"
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(16) |
|
(17) |
|
(18) |
|
(19) |
|
(20) |
|
(21) |
|
永続クラスの名前にインターフェースを指定してもまったく問題ありません。そのときは <subclass> 要素を使って、そのインターフェースを実装するクラスを定義してください。 static な内部クラスでも永続化できます。そのときは標準形式、例えば eg.Foo$Bar を使ってクラス名を指定してください。
mutable="false" 指定をした不変クラスは、アプリケーションによる更新や削除が出来ないことがあります。これにより、 Hibernate がパフォーマンスを少し改善します。
オプションの proxy 属性により、クラスの永続インスタンスの遅延初期化が可能になります。 Hibernate は最初に、指定したインターフェースを実装した CGLIB プロキシを返します。実際の永続オブジェクトはプロキシのメソッドを呼び出すときにロードします。以下の「遅延初期化のためのプロキシ」を参照してください。
暗黙的 ポリモーフィズムとは、次の二つを意味しています。一つはクラスのインスタンスが、スーパークラスや実装したインターフェース、またそのクラスを指定するクエリによって返されることで、もう一つはそのクラスのサブクラスのインスタンスが、そのクラス自身を指定したクエリによって返されることです。また、 明示的 ポリモーフィズムとは、次の二つを意味しています。一つはクラスのインスタンスが、そのクラスを明示的に指定したクエリによってのみ返されることで、もう一つはクラスを指定したクエリが、 <class> 要素の中で <subclass> や <joined-subclass> とマッピングされているサブクラスのインスタンスだけを返すことです。ほとんどの用途ではデフォルトの polymorphism="implicit" が適切です。明示的なポリモーフィズムは、2つの違ったクラスが同じテーブルにマッピングされているときに有用です (これによってテーブルカラムのサブセットを含む、「軽量な」クラスが可能になります)。
persister 属性を指定することで、クラスの永続化戦略をカスタマイズできます。例えば org.hibernate.persister.EntityPersister 自身のサブクラスを指定したり、また例えばストアドプロシージャコール、フラットファイルへシリアライズ、 LDAP などを通した永続性を実装する org.hibernate.persister.ClassPersister インターフェースの完全に新しい実装を提供できます。簡単な例として org.hibernate.test.CustomPersister を参照してください(これは Hashtable の「永続化」です)。
dynamic-update と dynamic-insert の設定はサブクラスに継承されません。そのため <subclass> や <joined-subclass> 要素を指定することも出来ます。これらの設定はパフォーマンスを向上させる事もありますが、落とすこともありますので、慎重に使用してください。
select-before-update の使用は通常パフォーマンスを落とします。もし Session へ分離インスタンスのグラフを再追加するなら、データベース更新のトリガを不必要に呼び出すのを避けるという点で、非常に有用です。
dynamic-update を有効にすれば、楽観ロック戦略を選ぶことになります:
version バージョン/タイムスタンプカラムをチェックします。
all すべてのカラムをチェックします。
dirty 変更したカラムをチェックし、同時更新できるようにします。
none 楽観ロックを使用しません。
Hibernate で楽観的ロック戦略を使うなら、バージョン/タイムスタンプカラムを使うことを 非常に 強くお勧めします。楽観的ロックはパフォーマンスの観点からも最適であり、さらに分離インスタンスへの修正 (つまり Session.marge() が使われるとき) を正確に扱うことのできる唯一の戦略でもあります。
Hibernate のマッピングにとってビューと普通のテーブルの間に違いはなく、データベースレベルでは透過的です(ただしビューを完全にはサポートしていない DBMS もあります。特に、更新のあるビューに対してはそうです)。ビューを使いたくても、データベースで作成できないことがあります(例えば、レガシースキーマの場合)。この場合には、不変かつ読み取り専用のエンティティに与えられた SQL の副問合せ文をマップできます:
<class name="Summary">
<subselect>
select item.name, max(bid.amount), count(*)
from item
join bid on bid.item_id = item.id
group by item.name
</subselect>
<synchronize table="item"/>
<synchronize table="bid"/>
<id name="name"/>
...
</class
>
テーブルをこのエンティティと同期するように定義してください。オートフラッシュが確実に起こるように、また導出エンティティに対するクエリが古いデータを返さないようにするためです。 <subselect> は属性とネストしたマッピング属性のどちらでも利用できます。
マップされたクラスはデータベーステーブルの主キーカラムを定義 しなければなりません 。ほとんどのクラスにはインスタンスのユニークな識別子を保持する JavaBeans スタイルのプロパティも持っています。 <id> 要素は、そのプロパティから主キーカラムへのマッピングを定義します。
<id
name="
propertyName"
type="
typename"
column
="column_name"
unsave
d-value="null|any|none|undefined|id_value"
access
="field|property|ClassName">
node="element-name|@attribute-name|element/@attribute|."
<generator class="generatorClass"/>
</id
>
|
|
|
|
|
|
|
|
|
|
name 属性がなければ、クラスには識別子プロパティがないものとみなされます。
unsaved-value 属性は Hibernate3 ではほとんどの場合、必要ではありません。
複合キーを持つレガシーデータにアクセスできるように、 <composite-id> という代替のマッピング定義があります。しかし他の用途への使用は全くおすすめできません。
オプションの <generator> 子要素は、永続クラスのインスタンスのユニークな識別子を生成するために使う、 Java クラスを指定します。ジェネレータインスタンスの設定、もしくは初期化にパラメータが必要であれば、 <param> 要素を使って渡すことができます。
<id name="id" type="long" column="cat_id">
<generator class="org.hibernate.id.TableHiLoGenerator">
<param name="table"
>uid_table</param>
<param name="column"
>next_hi_value_column</param>
</generator>
</id
>
すべてのジェネレータは、 org.hibernate.id.IdentifierGenerator インターフェースを実装します。これはとても単純なインターフェースなので、特別な実装を独自に用意するアプリケーションもあるかもしれません。しかし Hibernate は組み込みの実装をいくつも用意しています。組み込みのジェネレータには以下のショートカット名があります:
incrementlong , short , int 型の識別子を生成します。これらは他のプロセスが同じテーブルにデータを挿入しないときだけユニークです。 クラスタ内では使わないでください 。
identityDB2, MySQL, MS SQL Server, Sybase, HypersonicSQL の識別子カラムをサポートします。返される識別子の型は long , short , int のいずれかです。
sequenceDB2, PostgreSQL, Oracle, SAP DB, McKoi のシーケンスや、 Interbase のジェネレータを使用します。返される識別子の型は long , short , int のいずれかです。
hilolong , short , int 型の識別子を効率的に生成する hi/lo アルゴリズムを使います。 hi 値のソースとして、テーブルとカラムを与えます(デフォルトではそれぞれ hibernate_unique_key と next_hi )。 hi/lo アルゴリズムは特定のデータベースに対してのみユニークな識別子を生成します。
seqhilolong , short , int 型の識別子を効率的に生成する hi/lo アルゴリズムを使います。指定されたデータベースシーケンスを与えます。
uuid( IP アドレスが使用される)ネットワーク内でユニークな文字列型の識別子を生成するために、 128 ビットの UUID アルゴリズムを使用します。 UUID は長さ 32 の 16 進数字の文字列としてエンコードされます。
guidMS SQL サーバーと MySQL でデータベースが生成する GUID 文字列を使用します。
native使用するデータベースの性能により identity 、 sequence 、 hilo のいずれかが選ばれます。
assignedsave() が呼ばれる前に、アプリケーションがオブジェクトに識別子を代入できるようにします。 <generator> 要素が指定されていなければ、これがデフォルトの戦略になります。
selectあるユニークキーによる行の選択と主キーの値の復元により、データベーストリガが割り当てた主キーを取得します。
foreign他の関連オブジェクトの識別子を使います。普通は、 <one-to-one> 主キー関連と組み合わせて使います。
sequence-identity実際の値の生成のためにデータベースシーケンスを使用する特別なシーケンス生成戦略ですが、 JDBC3 getGeneratedKeys と結びついて、 INSERT 文の実行の一部として生成された識別子の値を実際に返します。この戦略は JDK 1.4 を対象とする Oracle 10g のドライバでサポートされていることが知られています。これらの INSERT 文でのコメントは Oracle のドライバのバグにより無効にされていることに注意してください。
hilo と seqhilo ジェネレータは、識別子生成の代表的なアプローチである hi/lo アルゴリズムの2つの代替実装を提供します。1番目の実装は、次回に利用される "hi" 値を保持する「特別な」データベーステーブルを必要とします。2番目の実装は、 Oracle スタイルのシーケンスを使います(サポートされている場合)。
<id name="id" type="long" column="cat_id">
<generator class="hilo">
<param name="table"
>hi_value</param>
<param name="column"
>next_value</param>
<param name="max_lo"
>100</param>
</generator>
</id
>
<id name="id" type="long" column="cat_id">
<generator class="seqhilo">
<param name="sequence"
>hi_value</param>
<param name="max_lo"
>100</param>
</generator>
</id
>
残念ながら Hibernate への独自の Connection を提供するときには、 hilo を使えません。 Hibernate が JTA でリストされている接続を取得するためにアプリケーションサーバーのデータソースを使用しているときには、 hibernate.transaction.manager_lookup_class を適切に設定しなければなりません。
UUID には以下のものが含まれます: IP アドレス、 JVM のスタートアップタイム(4分の1秒の正確さ)、システム時間、( JVM に対してユニークな)カウンタ値。 Java コードから MAC アドレスやメモリアドレスを取得することはできないので、 JNI が使えないときの最良の方法です。
識別子カラムをサポートしているデータベース(DB2, MySQL, Sybase, MS SQL)では、 identity キー生成が使えます。シーケンスをサポートするデータベース(DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB)では、 sequence スタイルのキー生成が使えます。どちらの戦略も、新しいオブジェクトを挿入するために、 SQL クエリを2つ必要とします。
<id name="id" type="long" column="person_id">
<generator class="sequence">
<param name="sequence"
>person_id_sequence</param>
</generator>
</id
>
<id name="id" type="long" column="person_id" unsaved-value="0">
<generator class="identity"/>
</id
>
クロスプラットフォームの開発では、native 戦略は identity 、 sequence 、 hilo 戦略の中から1つを選択しますが、これは使用しているデータベースの能力に依存します。
アプリケーションに識別子を割り当てさせたいのであれば( Hibernate が生成するものではなく)、 assigned ジェネレータを使うことができます。この特別なジェネレータは、すでにオブジェクトの識別子プロパティに代入された値を識別子に使います。このジェネレータは主キーが代理キーの代わりに自然キーである場合に使用します。 <generator> 要素を指定しない場合のデフォルトの動作になります。
assigned ジェネレータを選択すると、 Hibernate は unsaved-value="undefined" を使います。そして、バージョンやタイムスタンプのプロパティがない場合や Interceptor.isUnsaved() を定義しなかった場合には、インスタンスが一時的(transient)なものであるのか、またはセッションから分離(detached)したものかどうかを決めるために、データベースを調べます。
レガシースキーマのためにのみ指定します( Hibernate はトリガを使って DDL を生成しません)。
<id name="id" type="long" column="person_id">
<generator class="select">
<param name="key"
>socialSecurityNumber</param>
</generator>
</id
>
上記の例の中で、クラスで自然キーとして定義された socialSecurityNumber という名前のユニークな値のプロパティと、値がトリガにより生成される person_id という名前の代理キーがあります。
Starting with release 3.2.3, there are 2 new generators which represent a re-thinking of 2 different aspects of identifier generation. The first aspect is database portability; the second is optimization Optimization means that you do not have to query the database for every request for a new identifier value. These two new generators are intended to take the place of some of the named generators described above, starting in 3.3.x. However, they are included in the current releases and can be referenced by FQN.
The first of these new generators is org.hibernate.id.enhanced.SequenceStyleGenerator which is intended, firstly, as a replacement for the sequence generator and, secondly, as a better portability generator than native. This is because native generally chooses between identity and sequence which have largely different semantics that can cause subtle issues in applications eyeing portability. org.hibernate.id.enhanced.SequenceStyleGenerator, however, achieves portability in a different manner. It chooses between a table or a sequence in the database to store its incrementing values, depending on the capabilities of the dialect being used. The difference between this and native is that table-based and sequence-based storage have the same exact semantic. In fact, sequences are exactly what Hibernate tries to emulate with its table-based generators. This generator has a number of configuration parameters:
sequence_name (optional, defaults to hibernate_sequence): the name of the sequence or table to be used.
initial_value (optional, defaults to 1): the initial value to be retrieved from the sequence/table. In sequence creation terms, this is analogous to the clause typically named "STARTS WITH".
increment_size (optional - defaults to 1): the value by which subsequent calls to the sequence/table should differ. In sequence creation terms, this is analogous to the clause typically named "INCREMENT BY".
force_table_use (optional - defaults to false): should we force the use of a table as the backing structure even though the dialect might support sequence?
value_column (optional - defaults to next_val): only relevant for table structures, it is the name of the column on the table which is used to hold the value.
optimizer (optional - defaults to none): See 「Identifier generator optimization」
The second of these new generators is org.hibernate.id.enhanced.TableGenerator, which is intended, firstly, as a replacement for the table generator, even though it actually functions much more like org.hibernate.id.MultipleHiLoPerTableGenerator, and secondly, as a re-implementation of org.hibernate.id.MultipleHiLoPerTableGenerator that utilizes the notion of pluggable optimizers. Essentially this generator defines a table capable of holding a number of different increment values simultaneously by using multiple distinctly keyed rows. This generator has a number of configuration parameters:
table_name (optional - defaults to hibernate_sequences): the name of the table to be used.
value_column_name (optional - defaults to next_val): the name of the column on the table that is used to hold the value.
segment_column_name (optional - defaults to sequence_name): the name of the column on the table that is used to hold the "segment key". This is the value which identifies which increment value to use.
segment_value (optional - defaults to default): The "segment key" value for the segment from which we want to pull increment values for this generator.
segment_value_length (optional - defaults to 255): Used for schema generation; the column size to create this segment key column.
initial_value (optional - defaults to 1): The initial value to be retrieved from the table.
increment_size (optional - defaults to 1): The value by which subsequent calls to the table should differ.
optimizer (optional - defaults to ): See 「Identifier generator optimization」
For identifier generators that store values in the database, it is inefficient for them to hit the database on each and every call to generate a new identifier value. Instead, you can group a bunch of them in memory and only hit the database when you have exhausted your in-memory value group. This is the role of the pluggable optimizers. Currently only the two enhanced generators (「Enhanced identifier generators」 support this operation.
none (generally this is the default if no optimizer was specified): this will not perform any optimizations and hit the database for each and every request.
hilo: applies a hi/lo algorithm around the database retrieved values. The values from the database for this optimizer are expected to be sequential. The values retrieved from the database structure for this optimizer indicates the "group number". The increment_size is multiplied by that value in memory to define a group "hi value".
pooled: as with the case of hilo, this optimizer attempts to minimize the number of hits to the database. Here, however, we simply store the starting value for the "next group" into the database structure rather than a sequential value in combination with an in-memory grouping algorithm. Here, increment_size refers to the values coming from the database.
<composite-id
name="propertyName"
class="ClassName"
mapped="true|false"
access="field|property|ClassName">
node="element-name|."
<key-property name="propertyName" type="typename" column="column_name"/>
<key-many-to-one name="propertyName" class="ClassName" column="column_name"/>
......
</composite-id
>
複合キーのあるテーブルに対し、識別子プロパティとしてクラスの複数のプロパティをマッピングすることができます。 <composite-id> 要素は、子要素として <key-property> プロパティマッピングと <key-many-to-one> マッピングを受け入れます。
<composite-id>
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id
>
複合識別子の等価性を実装するためには、永続クラスが equals() と hashCode() をオーバーライド しなければなりません 。 また Serializable も実装しなければいけません。
残念ながら複合識別子のためのこの方法は、永続オブジェクトが自身の識別子であることを意味しています。オブジェクト自身を識別子とする以外に便利な「扱い方」はありません。複合キーに関連した永続状態を load() 出来るようになる前に、永続クラス自身をインスタンス化し、識別子プロパティを設定しなければなりません。 組み込みの 複合識別子と呼ばれるこのアプローチは、本格的なアプリケーションには向いていません。
2つ目の方法は マップされた 複合識別子と呼ばれるもので、 <composite-id>エレメント内で指定した識別プロパティが永続クラスと分離した識別子クラスの両方に重複して存在します。
<composite-id class="MedicareId" mapped="true">
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id
>
この例では、複合識別子クラス( MedicareId )とエンティティクラス自身の両方が、 medicareNumber と dependent という名前のプロパティを持ちます。識別子クラスは、 equals() と hashCode() をオーバライドし、 Serializable を実装しなくてはなりません。この方法には、明らかにコードが重複するという不都合があります。
次の属性はマッピングした複合識別子を指定するために使用します:
mapped (オプション、デフォルトは false ): マッピングした複合識別子が使用されることと、包含されたプロパティのマッピングが、エンティティクラスと複合識別子クラスの両方を参照することを示します。
class (オプション、ただしマッピングした複合識別子には必須): 複合識別子として使用するクラス。
We will describe a third, even more convenient approach, where the composite identifier is implemented as a component class in 「複合識別子としてのコンポーネント」. The attributes described below apply only to this alternative approach:
name (オプション、このアプローチでは必須): 複合識別子を保持するコンポーネントタイプのプロパティ(9章を参照してください)。
access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用すべき戦略。
class (オプション - デフォルトはリフレクションにより決定されるプロパティの型): 複合識別子として使われるコンポーネントのクラス(次の節を見てください)。
この3つ目の方法は 識別子コンポーネント と呼び、ほとんどすべてのアプリケーションに対して推奨する方法です。
<discriminator> 要素は、 table-per-class-hierarchy マッピング戦略を使うポリモーフィックな永続化に必要であり、テーブルの識別カラムを定義します。識別カラムは、ある行に対して永続層がどのサブクラスをインスタンス化するかを伝えるマーカー値を含んでいます。以下のような型に制限されます: string , character , integer, byte , short , boolean , yes_no , true_false.
<discriminator
column
="discriminator_column"
type="
discriminator_type"
force=
"true|false"
insert
="true|false"
formul
a="arbitrary sql expression"
/>
|
|
|
|
|
|
|
|
|
|
識別カラムの実際の値は、 <class> と <subclass> 要素の discriminator-value 属性で指定されます。
永続クラスへマッピングされない「余分な」識別値を持つ行がテーブルにあれば、(そのときに限り) force 属性は有効です。ただし、普通はそういうことはありません。
formula 属性を使うと、行の型を評価するために任意の SQL 式を宣言できます:
<discriminator
formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
type="integer"/>
<version> 要素はオプションであり、テーブルがバージョンデータを含むことを示します。これは ロングトランザクション を使うつもりなら、特に役立ちます(以下を参照してください)。
<version
column
="version_column"
name="
propertyName"
type="
typename"
access
="field|property|ClassName"
unsave
d-value="null|negative|undefined"
genera
ted="never|always"
insert
="true|false"
node="element-name|@attribute-name|element/@attribute|."
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
バージョン番号は Hibernate の long 、 integer 、 short 、 timestamp 、 calendar 型のいずれかです。
A version or timestamp property should never be null for a detached instance. Hibernate will detect any instance with a null version or timestamp as transient, irrespective of what other unsaved-value strategies are specified. Declaring a nullable version or timestamp property is an easy way to avoid problems with transitive reattachment in Hibernate. It is especially useful for people using assigned identifiers or composite keys.
オプションの <timestamp> 要素は、テーブルがタイムスタンプデータを含むことを示します。これはバージョン付けの代わりの方法として用意されています。タイムスタンプはもともと楽観的ロックにおける安全性の低い実装です。しかしアプリケーションはタイムスタンプを異なる用途で使うこともあるかもしれません。
<timestamp
column
="timestamp_column"
name="
propertyName"
access
="field|property|ClassName"
unsave
d-value="null|undefined"
source
="vm|db"
genera
ted="never|always"
node="element-name|@attribute-name|element/@attribute|."
/>
|
|
|
|
|
|
|
|
|
|
|
|
<timestamp> は <version type="timestamp"> と等価であることに注意してください。 <timestamp source="db"> は <version type="dbtimestamp"> と等価であることに注意してください。
<property> 要素は、クラスの永続的な JavaBean スタイルのプロパティを定義します。
<property
name="
propertyName"
column
="column_name"
type="
typename"
update
="true|false"
insert
="true|false"
formul
a="arbitrary SQL expression"
access
="field|property|ClassName"
lazy="
true|false"
unique
="true|false"
not-nu
ll="true|false"
optimi
stic-lock="true|false"
genera
ted="never|insert|always"
node="element-name|@attribute-name|element/@attribute|."
index="index_name"
unique_key="unique_key_id"
length="L"
precision="P"
scale="S"
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typename には以下の値が可能です:
Hibernate の基本型の名前(例 integer, string, character, date, timestamp, float, binary, serializable, object, blob )。
デフォルトの基本型の Java クラス名 (例 int, float, char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob )。
シリアライズ可能な Java クラスの名前。
カスタム型のクラス名(例 com.illflow.type.MyCustomType )。
型を指定しなければ、 Hibernate は正しい Hibernate の型を推測するために、指定されたプロパティに対してリフレクションを使います。 Hibernate はルール2, 3, 4をその順序に使い、 getter プロパティの返り値のクラスの名前を解釈しようとします。しかしこれで常に十分であるとは限りません。場合によっては、 type 属性が必要な場合があります。 (例えば Hibernate.DATE と Hibernate.TIMESTAMP を区別するため、またはカスタム型を指定するためなどです。)
access 属性で、実行時に Hibernate がどのようにプロパティにアクセスするかを制御できます。デフォルトでは Hibernate はプロパティの get/set のペアをコールします。 access="field" と指定すれば、 Hibernate はリフレクションを使い get/set のペアを介さずに、直接フィールドにアクセスします。インターフェース org.hibernate.property.PropertyAccessor を実装するクラスを指定することで、プロパティへのアクセスに独自の戦略を指定することができます。
特に強力な特徴は生成プロパティです。これらのプロパティは当然読み取り専用であり、プロパティの値はロード時に計算されます。計算を SQL 式として宣言すると、このプロパティはインスタンスをロードする SQL クエリの SELECT 句のサブクエリに変換されます:
<property name="totalPrice"
formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
WHERE li.productId = p.productId
AND li.customerId = customerId
AND li.orderNumber = orderNumber )"/>
特定のカラム(例では customerId がそれにあたります)のエイリアスを宣言することなく、エンティティ自身のテーブルを参照できることに注意してください。もし属性を使用したくなければ、ネストした <formula> マッピング要素を使えることにも注意してください。
他の永続クラスへの通常の関連は many-to-one 要素を使って定義します。リレーショナルモデルは多対一関連です。つまりあるテーブルの外部キーは、ターゲットとなるテーブルの主キーカラムを参照しています。
<many-to-one
name="
propertyName"
column
="column_name"
class=
"ClassName"
cascad
e="cascade_style"
fetch=
"join|select"
update
="true|false"
insert
="true|false"
proper
ty-ref="propertyNameFromAssociatedClass"
access
="field|property|ClassName"
unique
="true|false"
not-nu
ll="true|false"
optimi
stic-lock="true|false"
lazy="
proxy|no-proxy|false"
not-fo
und="ignore|exception"
entity
-name="EntityName"
formul
a="arbitrary SQL expression"
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
index="index_name"
unique_key="unique_key_id"
foreign-key="foreign_key_name"
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Setting a value of the cascade attribute to any meaningful value other than none will propagate certain operations to the associated object. The meaningful values are divided into three categories. First, basic operations, which include: persist, merge, delete, save-update, evict, replicate, lock and refresh; second, special values: delete-orphan; and third, all comma-separated combinations of operation names: cascade="persist,merge,evict" or cascade="all,delete-orphan". See 「連鎖的な永続化」 for a full explanation. Note that single valued, many-to-one and one-to-one, associations do not support orphan delete.
典型的な many-to-one 宣言は次のようにシンプルです。:
<many-to-one name="product" class="Product" column="PRODUCT_ID"/>
property-ref 属性は、外部キーが関連付けられたテーブルの、主キーでないユニークキーを参照しているレガシーデータをマップするためにだけ使うべきです。これは醜いリレーショナルモデルです。例えば Product クラスが、主キーでないユニークなシリアルナンバーを持っていると仮定してみてください。( unique 属性は SchemaExport ツールを使った Hibernate の DDL 生成を制御します。)
<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>
以下のように OrderItem に対してマッピングを使えます:
<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>
しかし、これは決して推奨できません。
参照したユニークキーが、関連するエンティティの多数のプロパティから構成される場合、指定した <properties> 要素内で、参照するプロパティをマッピングするべきです。
もし参照したユニークキーがコンポーネントのプロパティである場合は、プロパティのパスを指定できます:
<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>
他の永続クラスへの一対一関連は、one-to-one 要素で定義します。
<one-to-one
name="
propertyName"
class=
"ClassName"
cascad
e="cascade_style"
constr
ained="true|false"
fetch=
"join|select"
proper
ty-ref="propertyNameFromAssociatedClass"
access
="field|property|ClassName"
formul
a="any SQL expression"
lazy="
proxy|no-proxy|false"
entity
-name="EntityName"
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
foreign-key="foreign_key_name"
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
一対一関連には2種類あります:
主キー関連
ユニーク外部キー関連
主キー関連には、特別なテーブルカラムは必要ありません。もし2つの行が関連により関係していれば、2つのテーブルは同じ主キーの値を共有します。そのため2つのオブジェクトを主キー関連によって関連付けたいのであれば、確実に同じ識別子の値を代入しなければなりません。
主キー関連を行うためには、以下のマッピングを Employee と Person のそれぞれに追加してください。
<one-to-one name="person" class="Person"/>
<one-to-one name="employee" class="Employee" constrained="true"/>
ここで、 PERSON と EMPLOYEE テーブルの関係する行の主キーが同じであることを確実にしなければいけません。ここでは、 foreign という特殊な Hibernate 識別子生成戦略を使います:
<class name="person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="foreign">
<param name="property"
>employee</param>
</generator>
</id>
...
<one-to-one name="employee"
class="Employee"
constrained="true"/>
</class
>
Employee インスタンスが、 Person の employee プロパティで参照されるように、新しくセーブされた Person のインスタンスには同じ主キーの値が代入されます。新しくセーブする Person インスタンスは、その Person の employee プロパティが参照する Employee インスタンスとして同じ主キーが割り当てられます。
もう1つの方法として、 Employee から Person へのユニーク制約を使った外部キー関連は以下のように表現されます:
<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>
そしてこの関連は、以下の記述を Person のマッピングに追加することで双方向にすることができます:
<one-to-one name="employee" class="Employee" property-ref="person"/>
<natural-id mutable="true|false"/>
<property ... />
<many-to-one ... />
......
</natural-id
>
主キーとして代理キーの使用を推奨しますが、すべてのエンティティに対して自然キーを識別するようにすべきです。自然キーはユニークかつ非 null な一つのプロパティ、またはプロパティの連結です。不変であればさらに良いです。 <natural-id> 要素内で自然キーのプロパティをマッピングします。 Hibernate は必然的にユニークかつ null 値を許可する制約を生成し、こうしてマッピングはより自己記述的になります。
エンティティの自然キープロパティの比較には、 equals() と hashCode() の実装を強くお勧めします。
このマッピングは自然主キーを使ったエンティティでの使用を意図していません。
mutable (オプション、 デフォルトは false ): デフォルトでは、自然識別子プロパティは不変(定数)と想定されています。
<component> 要素は、子オブジェクトのプロパティを親クラスのテーブルのカラムへマッピングします。コンポーネントは自分のプロパティ、コンポーネント、コレクションの順に定義できます。以下の「コンポーネント」を見てください。
<component
name="
propertyName"
class=
"className"
insert
="true|false"
update
="true|false"
access
="field|property|ClassName"
lazy="
true|false"
optimi
stic-lock="true|false"
unique
="true|false"
node="element-name|."
>
<property ...../>
<many-to-one .... />
........
</component
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
子の <property> タグで、子のクラスのプロパティをテーブルカラムにマッピングします。
<component> 要素は、親エンティティへ戻る参照として、コンポーネントのクラスのプロパティをマッピングする <parent> サブ要素を許可します。
The <dynamic-component> element allows a Map to be mapped as a component, where the property names refer to keys of the map. See 「動的コンポーネント」 for more information.
<properties> 要素はクラスのプロパティの指定された、論理的なグルーピングを可能にします。この構造の最も重要な使用方法は、 property-ref のターゲットになるプロパティの結合を許可することです。それはまた、複数カラムのユニーク制約を定義する簡単な方法でもあります。
<properties
name="
logicalName"
insert
="true|false"
update
="true|false"
optimi
stic-lock="true|false"
unique
="true|false"
>
<property ...../>
<many-to-one .... />
........
</properties
>
|
|
|
|
|
|
|
|
|
|
例えば、もし以下のような <properties> マッピングがあった場合:
<class name="Person">
<id name="personNumber"/>
...
<properties name="name"
unique="true" update="false">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</properties>
</class
>
主キーの代わりに Person テーブルのユニークキーへの参照を持つ、レガシーデータの関連を持つかもしれません。:
<many-to-one name="person"
class="Person" property-ref="name">
<column name="firstName"/>
<column name="initial"/>
<column name="lastName"/>
</many-to-one
>
しかし、このようなレガシーデータマッピングのコンテキスト外への使用は推奨しません。
最後にポリモーフィックな永続化には、ルートの永続クラスの各サブクラスの定義が必要です。 table-per-class-hierarchy マッピング戦略では、 <subclass> 定義が使われます。
<subclass
name="
ClassName"
discri
minator-value="discriminator_value"
proxy=
"ProxyInterface"
lazy="
true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
entity-name="EntityName"
node="element-name"
extends="SuperclassName">
<property .... />
.....
</subclass
>
|
|
|
|
|
|
|
|
各サブクラスでは、永続プロパティとサブクラスを定義します。 <version> と <id> プロパティは、ルートクラスから継承されると仮定されます。階層構造におけるサブクラスは、ユニークな discriminator-value を定義しなければなりません。 none が指定されると、完全修飾された Java クラス名が使われます。
For information about inheritance mappings see 9章継承マッピング.
もう1つの方法として、各サブクラスを自身のテーブルへマッピングすることができます (table-per-subclass mapping strategy)。継承した状態はスーパークラスのテーブルを使った結合で検索します。 <joined-subclass> 要素を使用します。
<joined-subclass
name="
ClassName"
table=
"tablename"
proxy=
"ProxyInterface"
lazy="
true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
schema="schema"
catalog="catalog"
extends="SuperclassName"
persister="ClassName"
subselect="SQL expression"
entity-name="EntityName"
node="element-name">
<key .... >
<property .... />
.....
</joined-subclass
>
|
|
|
|
|
|
|
|
このマッピング戦略には、識別カラムは必要ありません。しかし各サブクラスは <key> 要素を使い、オブジェクト識別子を保持するテーブルカラムを定義しなければなりません。この章の初めのマッピングは以下のように書き直せます:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat" table="CATS">
<id name="id" column="uid" type="long">
<generator class="hilo"/>
</id>
<property name="birthdate" type="date"/>
<property name="color" not-null="true"/>
<property name="sex" not-null="true"/>
<property name="weight"/>
<many-to-one name="mate"/>
<set name="kittens">
<key column="MOTHER"/>
<one-to-many class="Cat"/>
</set>
<joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
<key column="CAT"/>
<property name="name" type="string"/>
</joined-subclass>
</class>
<class name="eg.Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping
>
For information about inheritance mappings see 9章継承マッピング.
3つ目の選択肢は、継承階層の具象クラスのみをテーブルにマッピングすることです (the table-per-concrete-class 戦略)。それぞれのテーブルは継承の状態を含めすべてのクラスの永続状態を定義します。 Hibernate ではその様な継承階層が必ずしも必要ではありません。単純にそれぞれのクラスを、別々の <class> 宣言を使ってマッピングすることができます。しかしポリモーフィックな関連 (例えば階層のスーパークラスへの関連) を使いたいなら、 <union-subclass> マッピングを使う必要があります。
<union-subclass
name="
ClassName"
table=
"tablename"
proxy=
"ProxyInterface"
lazy="
true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
schema="schema"
catalog="catalog"
extends="SuperclassName"
abstract="true|false"
persister="ClassName"
subselect="SQL expression"
entity-name="EntityName"
node="element-name">
<property .... />
.....
</union-subclass
>
|
|
|
|
|
|
|
|
このマッピング戦略では識別カラムやキーカラムは必要ありません。
For information about inheritance mappings see 9章継承マッピング.
テーブル間に一対一の関係があるとき、 <join> 要素を使うことで、1つのクラスのプロパティをいくつものテーブルにマッピングすることができます。
<join
table=
"tablename"
schema
="owner"
catalo
g="catalog"
fetch=
"join|select"
invers
e="true|false"
option
al="true|false">
<key ... />
<property ... />
...
</join
>
|
|
|
|
|
|
|
|
|
|
|
|
例えば人のアドレスの情報を分離したテーブルにマッピングすることが可能です (すべてのプロパティに対して値型のセマンティクスを保持します):
<class name="Person"
table="PERSON">
<id name="id" column="PERSON_ID"
>...</id>
<join table="ADDRESS">
<key column="ADDRESS_ID"/>
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</join>
...
この特徴はしばしばレガシーデータモデルに対してのみ有用ですが、クラスよりも少ないテーブルと、きめの細かいドメインモデルを推奨します。しかし後で説明するように、1つのクラス階層で継承のマッピング戦略を切り替える時には有用です。
今まで何度か <key> 要素が出てきました。この要素は新しいテーブルへの結合を定義したり、結合テーブルで外部キーを定義したりする親要素のどこにでも現れ、オリジナルテーブルの主キーを参照します。
<key
column
="columnname"
on-del
ete="noaction|cascade"
proper
ty-ref="propertyName"
not-nu
ll="true|false"
update
="true|false"
unique
="true|false"
/>
|
|
|
|
|
|
|
|
|
|
|
|
削除のパフォーマンスが重要であるシステムには、すべてのキーを on-delete="cascade" と定義することを推奨します。そうすることで Hibernate は、 DELETE 文を毎回発行する代わりに、データベースレベルの ON CASCADE DELETE 制約を使用します。この特徴はバージョン付けられたデータに対する Hibernate の通常の楽観的ロック戦略を無視するということに注意してください。
not-null と update 属性は、単方向一対多関連の時には有用です。単方向一対多関連を null を許容しない外部キーにマッピングするときは、 <key not-null="true"> を使ってキーカラムを宣言 しなくてはなりません 。
column 属性を記述できる任意のマッピング要素はまた、 <column> サブ要素も記述できます。同様に <formula> も formula 属性の代替手段です。
<column
name="column_name"
length="N"
precision="N"
scale="N"
not-null="true|false"
unique="true|false"
unique-key="multicolumn_unique_key_name"
index="index_name"
sql-type="sql_type_name"
check="SQL expression"
default="SQL expression"
read="SQL expression"
write="SQL expression"/>
<formula
>SQL expression</formula
>
Most of the attributes on column provide a means of tailoring the DDL during automatic schema generation. The read and write attributes allow you to specify custom SQL that Hibernate will use to access the column's value. For more on this, see the discussion of column read and write expressions.
The column and formula elements can even be combined within the same property or association mapping to express, for example, exotic join conditions.
<many-to-one name="homeAddress" class="Address"
insert="false" update="false">
<column name="person_id" not-null="true" length="10"/>
<formula
>'MAILING'</formula>
</many-to-one
>
アプリケーションに同じ名前の2つの永続クラスがあり、 Hibernate クエリで完全修飾された(パッケージの)名前を指定したくないと仮定します。そのような場合は auto-import="true" に頼らず、クラスが「インポート」されたものであると明示できます。明示的にマッピングされていないクラスやインターフェースでさえもインポートできます。
<import class="java.lang.Object" rename="Universe"/>
<import
class=
"ClassName"
rename
="ShortName"
/>
|
|
|
|
プロパティマッピングにはさらにもう1つの型があります。 <any> マッピング要素は、複数のテーブルからクラスへのポリモーフィックな関連を定義します。この型のマッピングには必ず複数のカラムが必要です。1番目のカラムは関連エンティティの型を保持します。残りのカラムは識別子を保持します。この種類の関連には外部キー制約を指定することはできません。そのためこれは最も使われることのない(ポリモーフィックな)関連のマッピング方法です。非常に特別な場合(例えば、検査ログやユーザーセッションデータなど)に限って、これを使うべきです。
meta-type により、アプリケーションはカスタム型を指定できます。このカスタム型はデータベースカラムの値を、 id-type で指定した型の識別子プロパティを持った永続クラスへマッピングします。 meta-type の値からクラス名へのマッピングを指定しなければなりません。
<any name="being" id-type="long" meta-type="string">
<meta-value value="TBL_ANIMAL" class="Animal"/>
<meta-value value="TBL_HUMAN" class="Human"/>
<meta-value value="TBL_ALIEN" class="Alien"/>
<column name="table_name"/>
<column name="id"/>
</any
>
<any
name="
propertyName"
id-typ
e="idtypename"
meta-t
ype="metatypename"
cascad
e="cascade_style"
access
="field|property|ClassName"
optimi
stic-lock="true|false"
>
<meta-value ... />
<meta-value ... />
.....
<column .... />
<column .... />
.....
</any
>
|
|
|
|
|
|
|
|
|
|
|
|
In relation to the persistence service, Java language-level objects are classified into two groups:
エンティティ はエンティティへの参照を保持する、他のすべてのオブジェクトから独立して存在します。参照されないオブジェクトがガベージコレクトされてしまう性質を持つ通常の Java モデルと、これを比べてみてください。(親エンティティから子へ、セーブと削除が カスケード されうることを除いて)エンティティは明示的にセーブまたは削除されなければなりません。これは到達可能性によるオブジェクト永続化の ODMG モデルとは異なっています。大規模なシステムでアプリケーションオブジェクトが普通どのように使われるかにより密接に対応します。エンティティは循環と参照の共有をサポートします。またそれらはバージョン付けすることもできます。
エンティティの永続状態は他のエンティティや 値 型のインスタンスへの参照から構成されます。値はプリミティブ、コレクション (コレクションの内部ではなく)、コンポーネント、不変オブジェクトです。エンティティとは違い、値は(特にコレクションとコンポーネントにおいて)、到達可能性による永続化や削除が 行われます 。値オブジェクト(とプリミティブ)は、包含するエンティティと一緒に永続化や削除が行われるので、それらを独立にバージョン付けすることはできません。値には独立したアイデンティティがないので、複数のエンティティやコレクションがこれを共有することはできません。
これまで「永続クラス」という言葉をエンティティの意味で使ってきました。これからもそうしていきます。厳密に言うと、永続状態を持つユーザー定義のクラスのすべてがエンティティというわけではありません。 コンポーネント は値のセマンティクスを持つユーザー定義クラスです。 java.lang.String 型のプロパティもまた値のセマンティクスを持ちます。定義するなら、 JDK で提供されているすべての Java の型 (クラス) が値のセマンティクスを持つといえます。一方ユーザー定義型は、エンティティや値型のセマンティクスとともにマッピングできます。この決定はアプリケーション開発者次第です。そのクラスの1つのインスタンスへの共有参照は、ドメインモデル内のエンティティクラスに対する良いヒントになります。一方合成集約や集約は、通常値型へ変換されます。
本ドキュメントを通して、何度もこの概念を取り上げます。
Java 型のシステム (もしくは開発者が定義したエンティティと値型) を SQL /データベース型のシステムにマッピングすることは難しいです。 Hibernate は2つのシステムの架け橋を提供します。エンティティに対しては <class> や <subclass> などを使用します。値型に対しては <property> や <component> などを、通常 type と共に使います。この属性の値は Hibernate の マッピング型 の名前です。 Hibernate は (標準 JDK の値型に対して) 多くの自由なマッピングを提供します。後で見るように、自身のマッピング型を記述し、同様にカスタムの変換戦略を実装することができます。
コレクションを除く組み込みの Hibernate の型はすべて、 null セマンティクスをサポートします。
組み込みの 基本的なマッピング型 は大まかに以下のように分けられます。
integer, long, short, float, double, character, byte, boolean, yes_no, true_falseJava のプリミティブやラッパークラスから適切な(ベンダー固有の) SQL カラム型への型マッピング。 boolean, yes_no と true_false は、すべて Java の boolean または java.lang.Boolean の代替エンコードです。
stringjava.lang.String から VARCHAR (または Oracle の VARCHAR2 )への型マッピング。
date, time, timestampjava.util.Date とそのサブクラスから SQL 型の DATE 、 TIME 、 TIMESTAMP (またはそれらと等価なもの) への型マッピング。
calendar, calendar_datejava.util.Calendar から SQL 型 の「 TIMESTAMP 、 DATE (またはそれらと等価なもの)への型マッピング。
big_decimal, big_integerjava.math.BigDecimal と java.math.BigInteger から NUMERIC(または Oracle の NUMBER )への型マッピング。
locale, timezone, currencyjava.util.Locale 、 java.util.TimeZone 、 java.util.Currency から VARCHAR (または Oracle の VARCHAR2 )への型マッピング。 Locale と Currency のインスタンスは、それらの ISO コードにマッピングされます。 TimeZone のインスタンスは、それらの ID にマッピングされます。
classjava.lang.Class から VARCHAR (または Oracle の VARCHAR2 )への型マッピング。 Class はその完全修飾された名前にマッピングされます。
binaryバイト配列は、適切な SQL のバイナリ型にマッピングされます。
text長い Java 文字列は、 SQL の CLOB または TEXT 型にマッピングされます。
serializableシリアライズ可能な Java 型は、適切な SQL のバイナリ型にマッピングされます。デフォルトで基本型ではないシリアライズ可能な Java クラスやインターフェースの名前を指定することで、 Hibernate の型を serializable とすることもできます。
clob, blobJDBC クラス java.sql.Clob と java.sql.Blob に対する型マッピング。 blob や clob オブジェクトはトランザクションの外では再利用できないため、アプリケーションによっては不便かもしれません。(さらにはドライバサポートが一貫していません。)
imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date, imm_serializable, imm_binary
ほとんどの場合に可変である Java の型に対する型マッピング。 Hibernate は不変な Java の型に対しては最適化を行い、アプリケーションはそれを不変オブジェクトとして扱います。例えば imm_timestamp としてマップしたインスタンスに対して、 Date.setTime() を呼び出してはなりません。プロパティの値を変更しその変更を永続化するためには、アプリケーションはプロパティに対して新しい (同一でない) オブジェクトを割り当てなければなりません。
エンティティとコレクションのユニークな識別子は、 binary 、 blob 、 clob を除く、どんな基本型でも構いません。(複合識別子でも構いません。以下を見てください。)
基本的な値型には、 org.hibernate.Hibernate で定義された Type 定数がそれぞれあります。例えば、 Hibernate.STRING は string 型を表現しています。
開発者が独自の値型を作成することは、比較的簡単です。例えば、 java.lang.BigInteger 型のプロパティを VARCHAR カラムに永続化したいかもしれません。 Hibernate はこのための組み込み型を用意していません。しかしカスタム型は、プロパティ(またはコレクションの要素)を1つのテーブルカラムにマッピングするのに制限はありません。そのため例えば、 java.lang.String 型の getName() / setName() Java プロパティを FIRST_NAME 、 INITIAL 、 SURNAME カラムに永続化できます。
カスタム型を実装するには、 org.hibernate.UserType または org.hibernate.CompositeUserType を実装し、型の完全修飾された名前を使ってプロパティを定義します。どのような種類のものが可能かを調べるには、 org.hibernate.test.DoubleStringType を確認してください。
<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
<column name="first_string"/>
<column name="second_string"/>
</property
>
<column> タグで、プロパティを複数のカラムへマッピングできることに注目してください。
CompositeUserType 、 EnhancedUserType 、 UserCollectionType 、 UserVersionType インターフェースは、より特殊な使用法に対してのサポートを提供します。
マッピングファイル内で UserType へパラメータを提供できます。このためには、 UserType は org.hibernate.usertype.ParameterizedType を実装しなくてはなりません。カスタム型パラメータを提供するために、マッピングファイル内で <type> 要素を使用できます。
<property name="priority">
<type name="com.mycompany.usertypes.DefaultValueIntegerType">
<param name="default"
>0</param>
</type>
</property
>
UserType は、引数として渡された Properties オブジェクトから、 default で指定したパラメータに対する値を検索することができます。
特定の UserType を頻繁に使用するならば、短い名前を定義すると便利になるでしょう。 <typedef> 要素を使ってこのようなことが行えます。 Typedefs はカスタム型に名前を割り当てます。その型がパラメータを持つならば、パラメータのデフォルト値のリストを含むこともできます。
<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
<param name="default"
>0</param>
</typedef
>
<property name="priority" type="default_zero"/>
プロパティのマッピングで型パラメータを使うことで、 typedef で提供されたパラメータをその都度オーバーライドすることが可能です。
Even though Hibernate's rich range of built-in types and support for components means you will rarely need to use a custom type, it is considered good practice to use custom types for non-entity classes that occur frequently in your application. For example, a MonetaryAmount class is a good candidate for a CompositeUserType, even though it could be mapped as a component. One reason for this is abstraction. With a custom type, your mapping documents would be protected against changes to the way monetary values are represented.
ある永続クラスに、一つ以上のマッピングを提供することが出来ます。この場合、マッピングする2つのエンティティのインスタンスを明確にするために、 エンティティ名 を指定しなければなりません (デフォルトではエンティティ名はクラス名と同じです。)。 Hibernate では、永続オブジェクトを扱うとき、クエリを書き込むとき、指定されたエンティティへの関連をマッピングするときに、エンティティ名を指定しなければなりません。
<class name="Contract" table="Contracts"
entity-name="CurrentContract">
...
<set name="history" inverse="true"
order-by="effectiveEndDate desc">
<key column="currentContractId"/>
<one-to-many entity-name="HistoricalContract"/>
</set>
</class>
<class name="Contract" table="ContractHistory"
entity-name="HistoricalContract">
...
<many-to-one name="currentContract"
column="currentContractId"
entity-name="CurrentContract"/>
</class
>関連が class の代わりに entity-name を使って、どのように指定されるのかに注目してください。
マッピングドキュメントでテーブルやカラムの名前をバッククォートで囲むことで、 Hibernate で生成された SQL 中の識別子を引用させることができます。 Hibernate は SQL の Dialect に対応する、正しい引用スタイルを使います(普通はダブルクォートですが、 SQL Server ではかぎ括弧、 MySQL ではバッククォートです)。
<class name="LineItem" table="`Line Item`">
<id name="id" column="`Item Id`"/><generator class="assigned"/></id>
<property name="itemNumber" column="`Item #`"/>
...
</class
>
XML の記述以外に、 Hibernate では O/R マッピングのメタデータを定義する代替方法があります。
多くの Hibernate ユーザーは XDoclet の @hibernate.tags を使って、ソースコード内に直接マッピング情報を埋め込むことを好みます。これは厳密に言えば XDoclet の分野なので、本ドキュメントではこの方法を対象とはしません。しかし XDoclet を使った以下の Cat マッピングの例を示します。
package eg;
import java.util.Set;
import java.util.Date;
/**
* @hibernate.class
* table="CATS"
*/
public class Cat {
private Long id; // identifier
private Date birthdate;
private Cat mother;
private Set kittens
private Color color;
private char sex;
private float weight;
/*
* @hibernate.id
* generator-class="native"
* column="CAT_ID"
*/
public Long getId() {
return id;
}
private void setId(Long id) {
this.id=id;
}
/**
* @hibernate.many-to-one
* column="PARENT_ID"
*/
public Cat getMother() {
return mother;
}
void setMother(Cat mother) {
this.mother = mother;
}
/**
* @hibernate.property
* column="BIRTH_DATE"
*/
public Date getBirthdate() {
return birthdate;
}
void setBirthdate(Date date) {
birthdate = date;
}
/**
* @hibernate.property
* column="WEIGHT"
*/
public float getWeight() {
return weight;
}
void setWeight(float weight) {
this.weight = weight;
}
/**
* @hibernate.property
* column="COLOR"
* not-null="true"
*/
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
/**
* @hibernate.set
* inverse="true"
* order-by="BIRTH_DATE"
* @hibernate.collection-key
* column="PARENT_ID"
* @hibernate.collection-one-to-many
*/
public Set getKittens() {
return kittens;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kittens.add(kitten);
}
/**
* @hibernate.property
* column="SEX"
* not-null="true"
* update="false"
*/
public char getSex() {
return sex;
}
void setSex(char sex) {
this.sex=sex;
}
}
Hibernate のウェブサイトには、 XDoclet と Hibernate に関するサンプルが多数あります。
JDK5.0 ではタイプセーフかつコンパイル時にチェックできる、言語レベルの XDoclet スタイルのアノテーションを導入しました。このメカニズムは XDoclet のアノテーションよりも強力で、ツールや IDE も多くがサポートしています。例えば IntelliJ IDEA は、 JDK5.0 にアノテーションの自動補完と構文の強調表示をサポートしています。 EJB 仕様 (JSR-220) の新しいバージョンでは、エンティティ Bean に対する主要なメタデータメカニズムとして JDK5.0 のアノテーションを使用しています。 Hibernate3 では JSR-220 (永続化 API) の EntityManager を実装し、メタデータマッピングに対するサポートは、別ダウンロードの Hibernate Annotations パッケージにより利用可能です。これは EJB3 (JSR-220) と Hibernate3 のメタデータをどちらもサポートしています。
以下は EJB のエンティティ Bean として注釈された POJO クラスの例です:
@Entity(access = AccessType.FIELD)
public class Customer implements Serializable {
@Id;
Long id;
String firstName;
String lastName;
Date birthday;
@Transient
Integer age;
@Embedded
private Address homeAddress;
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="CUSTOMER_ID")
Set<Order
> orders;
// Getter/setter and business methods
}
JDK5.0 のアノテーション (と JSR-220) のサポートは進行中の作業であり、完全ではないことに注意してください。さらに詳しい情報は Hibernate のアノテーションモジュールを参照してください。
生成プロパティとは、データベースによって生成された値を持つプロパティです。通常、 Hibernate アプリケーションは、データベースが値を生成したプロパティを含むオブジェクトを リフレッシュ する必要がありました。しかし、プロパティが生成されたということをマークすることで、アプリケーションはリフレッシュの責任を Hibernate に委譲します。基本的に、生成プロパティを持つと定義したエンティティに対して Hibernate が INSERT や UPDATE の SQL を発行した後すぐに、生成された値を読み込むための SELECT SQL が発行されます。
Properties marked as generated must additionally be non-insertable and non-updateable. Only versions, timestamps, and simple properties, can be marked as generated.
never (デフォルト) - 与えられたプロパティの値は、データベースから生成されないことを意味します。
insert: the given property value is generated on insert, but is not regenerated on subsequent updates. Properties like created-date fall into this category. Even though version and timestamp properties can be marked as generated, this option is not available.
always - 挿入時も更新時もプロパティの値が生成されることを示します。
Hibernate allows you to customize the SQL it uses to read and write the values of columns mapped to simple properties. For example, if your database provides a set of data encryption functions, you can invoke them for individual columns like this:
<!-- XML : generated by JHighlight v1.0 (http://jhighlight.dev.java.net) --> <span class="xml_tag_symbols"><</span><span class="xml_tag_name">property</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"creditCardNumber"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">column</span><span class="xml_plain"> </span><br /> <span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"credit_card_num"</span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_attribute_name">read</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"decrypt(credit_card_num)"</span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_attribute_name">write</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"encrypt(?)"</span><span class="xml_tag_symbols">/></span><span class="xml_plain"></span><br /> <span class="xml_tag_symbols"></</span><span class="xml_tag_name">property</span><span class="xml_plain"></span><br /> <span class="xml_tag_symbols">></span><span class="xml_plain"></span><br />
Hibernate applies the custom expressions automatically whenever the property is referenced in a query. This functionality is similar to a derived-property formula with two differences:
The property is backed by one or more columns that are exported as part of automatic schema generation.
The property is read-write, not read-only.
The write expression, if specified, must contain exactly one '?' placeholder for the value.
Hibernate のスキーマエボリューションツールと連動することで、任意のデータベースオブジェクト(トリガーやストアドプロシージャなど)の CREATE と DROP により、 Hibernate のマッピングファイル内のユーザースキーマをすべて定義することが出来ます。主にトリガやストアドプロシージャのようなデータベースオブジェクトを生成や削除することを意図していますが、実際には java.sql.Statement.execute() メソッドによって実行できる任意の SQL コマンド(ALTER、INSERTなど)が実行できます。補助的なデータベースオブジェクトを定義するための、2つの基本的な方法があります。
1つ目の方法は、 CREATE と DROP コマンドをマッピングファイルの外に、明示的に記載することです:
<hibernate-mapping>
...
<database-object>
<create
>CREATE TRIGGER my_trigger ...</create>
<drop
>DROP TRIGGER my_trigger</drop>
</database-object>
</hibernate-mapping
>
2つ目の方法は、 CREATE と DROP コマンドの組み立て方を知っているカスタムクラスを提供することです。このカスタムクラスは org.hibernate.mapping.AuxiliaryDatabaseObject インタフェースを実装しなければなりません。
<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
</database-object>
</hibernate-mapping
>
さらに、あるデータベース方言が使用される時にだけ適用するといったように、データベースオブジェクトが使われるケースを限定できます。
<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
<dialect-scope name="org.hibernate.dialect.Oracle9iDialect"/>
<dialect-scope name="org.hibernate.dialect.Oracle10gDialect"/>
</database-object>
</hibernate-mapping
>
コレクション型のフィールドを永続化するには、そのコレクション型がインターフェース型である必要があります。例えば、
public class Product {
private String serialNumber;
private Set parts = new HashSet();
public Set getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
}
実在するインターフェースには java.util.Set、 java.util.Collection、 java.util.List、 java.util.Map、 java.util.SortedSet、 java.util.SortedMap などがあります。または、任意のインターフェースが使えます。 (ただし、「任意のインターフェース」を使用する場合は、 org.hibernate.usertype.UserCollectionType の実装クラスを作成する必要があります。)
HashSet のインスタンスを持つインスタンス変数がどのように初期化されるかに注目してみましょう。これは新たに生成された(永続化されていない)コレクション型のプロパティを初期化する最適な方法です。 (例えば persist() により)インスタンスを永続化しようとしたとき、 Hibernate は HashSet を Hibernate 独自の Set の実装クラスに置き換えます。このため、次のようなエラーには注意が必要です。
Cat cat = new DomesticCat();
Cat kitten = new DomesticCat();
....
Set kittens = new HashSet();
kittens.add(kitten);
cat.setKittens(kittens);
session.persist(cat);
kittens = cat.getKittens(); // Okay, kittens collection is a Set
(HashSet) cat.getKittens(); // Error!
Hibernate により注入された永続性コレクションは、インターフェース型に応じて、 HashMap や HashSet、 TreeMap、 TreeSet、 ArrayList のように振舞います。
コレクションインスタンスは、値型として普通に振舞います。永続化オブジェクトに参照されたときに自動的に永続化され、参照がなくなったときに自動的に削除されます。もしある永続化オブジェクトから別の永続化オブジェクトに渡されたら、その要素は現在のテーブルから別のテーブルに移動するかもしれません。2つのエンティティが同じコレクションインスタンスを共有してはいけません。リレーショナルモデルをベースにしているため、コレクション型のプロパティに null 値を代入しても意味がありません。つまり Hibernate は参照先のないコレクションと空のコレクションを区別しません。
しかしそれほど心配しなくても構いません。普段使っている Java のコレクションと同じように、永続化コレクションを使ってください。双方向関連の意味を理解すればよいのです(これは後ほど説明します)。
多くの一般的なリレーショナルモデルをカバーしたために、コレクションのために利用できるマッピングにはかなりの幅があります。様々なマッピング宣言がどのようにデータベーステーブルに変換されるかを知るために、スキーマ生成ツールを使ってみると良いでしょう。
コレクションをマッピングするためのマッピング要素は、インターフェースの型に依存します。例えば、 <set> 要素は Set 型のプロパティをマッピングするために使います。
<class name="Product">
<id name="serialNumber" column="productSerialNumber"/>
<set name="parts">
<key column="productSerialNumber" not-null="true"/>
<one-to-many class="Part"/>
</set>
</class
>
マッピング要素には <set> の他に <list>、 <map>、 <bag>、 <array>、 <primitive-array> があります。代表として、 <map> 要素を下記に示します。
<map
name="prop
ertyName"
table="tab
le_name"
schema="sc
hema_name"
lazy="true
|extra|false"
inverse="t
rue|false"
cascade="a
ll|none|save-update|delete|all-delete-orphan|delete-orphan"
sort="unso
rted|natural|comparatorClass"
order-by="
column_name asc|desc"
where="arb
itrary sql where condition"
fetch="joi
n|select|subselect"
batch-size
="N"
access="fi
eld|property|ClassName"
optimistic
-lock="true|false"
mutable="t
rue|false"
node="element-name|."
embed-xml="true|false"
>
<key .... />
<map-key .... />
<element .... />
</map
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
コレクションのインスタンスは、データベース内では、そのコレクションを所有するエンティティの外部キーによって識別されます。この外部キーはコレクションテーブルの コレクションキーカラム (またはカラム) と呼ばれます。コレクションキーカラムは <key> 要素によりマッピングします。
外部キーカラムには null 設定制約があるかもしれません。ほとんどのコレクションに当てはまるでしょう。単方向の一対多関連において、外部キーカラムはデフォルトで null を許す設定になっています。よって、 not-null="true" を指定する必要があるかもしれません。
<key column="productSerialNumber" not-null="true"/>
外部キーの制約が ON DELETE CASCADE を使うかもしれません。
<key column="productSerialNumber" on-delete="cascade"/>
<key> 要素のすべての定義については前の章を参照してください。
コレクションは他の Hibernate の型のほとんど(すべての基本型、カスタム型、コンポーネント、他のエンティティへの参照)を格納することができます。次の点は重要な違いになります。コレクションに格納されたオブジェクトが「値」セマンティクスとして扱われるのか (ライフサイクルはコレクションのオーナーに完全に依存します)、もしくはそれ自身のライフサイクルを持った別のエンティティへの参照であるかのかという違いです。後者は、2つのオブジェクト間の「リンク」をコレクションに保持していると見なしているだけです。
格納される型は コレクション要素型 と呼ばれます。コレクション要素は、 <element> または <composite-element> によりマッピングされ、エンティティへの参照の場合には <one-to-many> または <many-to-many> によりマッピングされます。最初の二つは値として要素をマッピングし、次の二つはエンティティの関連をマッピングするのに使われます。
set と bag を除く全てのコレクションマッピングには、コレクションテーブルの中に インデックス用のカラム が必要です。そのカラムに、配列や List のインデックス、もしくは Map のキーをマッピングします。 Map のインデックスは、 <map-key> によりマッピングされた基本型か、 <map-key-many-to-many> によりマッピングされたエンティティの関連か、あるいは <composite-map-key> によりマッピングされたコンポジット型になります。配列かリストのインデックスは、常に integer 型で、 <list-index> 要素によりマッピングします。マッピングされたカラムにはシーケンシャルな整数を格納します (デフォルトでは0から番号が付けられます)。
<list-index
column
="column_name"
base="
0|1|..."/>
| |
| |
<map-key
column
="column_name"
formul
a="any SQL expression"
type="
type_name"
node="@attribute-name"
length="N"/>
| |
| |
| |
<map-key-many-to-many
column
="column_name"
formul
a="any SQL expression"
class="ClassName"
/>
| |
| |
| |
If your table does not have an index column, and you still wish to use List as the property type, you can map the property as a Hibernate <bag>. A bag does not retain its order when it is retrieved from the database, but it can be optionally sorted or ordered.
値のコレクションや多対多関連は、専用の コレクションテーブル が必要です。このテーブルは、外部キーカラムと、 コレクション要素のカラム と、場合によってはインデックスカラムを持ちます。
値のコレクションのために、 <element> タグを使用します。
<element
column
="column_name"
formul
a="any SQL expression"
type="
typename"
length="L"
precision="P"
scale="S"
not-null="true|false"
unique="true|false"
node="element-name"
/>
| |
| |
| |
A many-to-many association is specified using the <many-to-many> element.
<many-to-many
column
="column_name"
formul
a="any SQL expression"
class=
"ClassName"
fetch=
"select|join"
unique
="true|false"
not-fo
und="ignore|exception"
entity
-name="EntityName"
proper
ty-ref="propertyNameFromAssociatedClass"
node="element-name"
embed-xml="true|false"
/>
| |
| |
| |
| |
| |
| |
| |
| |
Here are some examples.
A set of strings:
<set name="names" table="person_names">
<key column="person_id"/>
<element column="person_name" type="string"/>
</set
>
整数値を含む bag (bagは order-by 属性によって反復順序が定義されています):
<bag name="sizes"
table="item_sizes"
order-by="size asc">
<key column="item_id"/>
<element column="size" type="integer"/>
</bag
>
エンティティの配列 - この場合、多対多の関連です。
<array name="addresses"
table="PersonAddress"
cascade="persist">
<key column="personId"/>
<list-index column="sortOrder"/>
<many-to-many column="addressId" class="Address"/>
</array
>
文字列と日付の map
<map name="holidays"
table="holidays"
schema="dbo"
order-by="hol_name asc">
<key column="id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map
>
コンポーネントの list (次の章で詳しく説明します)
<list name="carComponents"
table="CarComponents">
<key column="carId"/>
<list-index column="sortOrder"/>
<composite-element class="CarComponent">
<property name="price"/>
<property name="type"/>
<property name="serialNumber" column="serialNum"/>
</composite-element>
</list
>
一対多関連 は、コレクションテーブルを介さず、外部キーにより2つのクラスのテーブルを関連付けます。このマッピングは標準的な Java のコレクションのセマンティクスをいくつか失います:
エンティティクラスのインスタンスは、2つ以上のコレクションのインスタンスに属してはいけません。
コレクションに含まれるエンティティクラスのインスタンスは、コレクションインデックスの値として2度以上現れてはいけません。
Product から Part への関連は、 Part テーブルへの外部キーカラムと、場合によってはインデックスカラムが必要です。 <one-to-many> タグは、これが一対多関連であることを表しています。
<one-to-many
class=
"ClassName"
not-fo
und="ignore|exception"
entity
-name="EntityName"
node="element-name"
embed-xml="true|false"
/>
|
|
|
|
|
|
<one-to-many> 要素はカラムを宣言する必要がないことに注意してください。同様に テーブル 名を指定する必要もありません。
If the foreign key column of a <one-to-many> association is declared NOT NULL, you must declare the <key> mapping not-null="true" or use a bidirectional association with the collection mapping marked inverse="true". See the discussion of bidirectional associations later in this chapter for more information.
次の例は、名称(Part の永続的なプロパティである partName) による Part エンティティの map を表しています。 formula によるインデックスを使っていることに注意してください。
<map name="parts"
cascade="all">
<key column="productId" not-null="true"/>
<map-key formula="partName"/>
<one-to-many class="Part"/>
</map
>
Hibernate は java.util.SortedMap と java.util.SortedSet を実装したコレクションをサポートしています。開発者はマッピング定義ファイルにコンパレータを指定しなければなりません:
<set name="aliases"
table="person_aliases"
sort="natural">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" sort="my.custom.HolidayComparator">
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map
>
sort 属性に設定できる値は unsorted と natural および、 java.util.Comparator を実装したクラスの名前です。
ソートされたコレクションは実質的には java.util.TreeSet や java.util.TreeMap のように振舞います。
もしデータベース自身にコレクションの要素を並べさせたいなら、 set や bag、map の order-by 属性を使います。この解決法は JDK1.4 、もしくはそれ以上のバージョンで利用可能です (LinkedHashSet または LinkedHashMapを使って実装されています)。整列はメモリ上ではなく、 SQL クエリ内で実行されます。
<set name="aliases" table="person_aliases" order-by="lower(name) asc">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" order-by="hol_date, hol_name">
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date type="date"/>
</map
>
The value of the order-by attribute is an SQL ordering, not an HQL ordering.
関連は、コレクションの filter() を使うことで、実行時に任意の criteria によってソートすることも可能です。
sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();
双方向関連 は関連のどちら「側」からでもナビゲーションできます。2種類の双方向関連がサポートされています:
片側が set か bag 、もう片方が単一値です。
両側が set か bag です。
2つの多対多関連で同じデータベーステーブルをマッピングし、片方を inverse として宣言することで、双方向の多対多関連を指定することが出来ます (どちらを inverse に選んだとしても、そちら側にはインデックス付きのコレクションは使えません)。
次に双方向の多対多関連の例を示します。各カテゴリは多数のアイテムを持つことができ、各アイテムは多くのカテゴリに属することが出来ます。
<class name="Category">
<id name="id" column="CATEGORY_ID"/>
...
<bag name="items" table="CATEGORY_ITEM">
<key column="CATEGORY_ID"/>
<many-to-many class="Item" column="ITEM_ID"/>
</bag>
</class>
<class name="Item">
<id name="id" column="ITEM_ID"/>
...
<!-- inverse end -->
<bag name="categories" table="CATEGORY_ITEM" inverse="true">
<key column="ITEM_ID"/>
<many-to-many class="Category" column="CATEGORY_ID"/>
</bag>
</class
>
関連の inverse 側にのみ行われた変更は永続化 されません。これは、 Hibernate は全ての双方向関連について、メモリ上に2つの表現を持っているという意味です。つまり一つは A から B へのリンクで、もう一つは B から A へのリンクということです。 Java のオブジェクトモデルについて考え、 Java で双方向関係をどうやって作るかを考えれば、これは理解しやすいです。下記に、 Java での双方向関連を示します。
category.getItems().add(item); // The category now "knows" about the relationship
item.getCategories().add(category); // The item now "knows" about the relationship
session.persist(item); // The relationship won't be saved!
session.persist(category); // The relationship will be saved
関連の inverse ではない側は、メモリ上の表現をデータベースに保存するのに使われます。
双方向の一対多関連を定義するには、一対多関連を多対一関連と同じテーブルのカラムにマッピングし、多側に inverse="true" と宣言します。
<class name="Parent">
<id name="id" column="parent_id"/>
....
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</class
>
関連の片側に inverse="true" を設定しても、カスケード操作に影響を与えません。これらは直交した概念です。
片側が <list> や <map> である双方向関連は、特によく考える必要があります。インデックスカラムにマップされる子クラスのプロパティがある場合は、問題ないです。コレクションのマッピングで inverse="true" を使い続けられます。
<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children" inverse="true">
<key column="parent_id"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<property name="name"
not-null="true"/>
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</class
>
しかし、子クラスにそのようなプロパティがない場合は、関連を真に双方向であると考えることができません (関連の片側に利用できる情報がありますが、もう一方にはありません)。この場合は、コレクションに inverse="true" をマッピングできません。代わりに、次のようなマッピングが使えます:
<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children">
<key column="parent_id"
not-null="true"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
insert="false"
update="false"
not-null="true"/>
</class
>
Note that in this mapping, the collection-valued end of the association is responsible for updates to the foreign key.
3項関連のマッピングには3つのアプローチがあります。1つ目は関連をインデックスとして Map を使用するアプローチです:
<map name="contracts">
<key column="employer_id" not-null="true"/>
<map-key-many-to-many column="employee_id" class="Employee"/>
<one-to-many class="Contract"/>
</map
>
<map name="connections">
<key column="incoming_node_id"/>
<map-key-many-to-many column="outgoing_node_id" class="Node"/>
<many-to-many column="connection_id" class="Connection"/>
</map
>
2つ目は単純に関連をエンティティクラスとしてモデルを作り直すアプローチで、頻繁に使われます。
最後は composite 要素を使うアプローチです。これに関する議論は後ほど行います。
The majority of the many-to-many associations and collections of values shown previously all map to tables with composite keys, even though it has been have suggested that entities should have synthetic identifiers (surrogate keys). A pure association table does not seem to benefit much from a surrogate key, although a collection of composite values might. It is for this reason that Hibernate provides a feature that allows you to map many-to-many associations and collections of values to a table with a surrogate key.
bag のセマンティックスを持った List(または Collection)を <idbag> 要素にマッピングできます。
<idbag name="lovers" table="LOVERS">
<collection-id column="ID" type="long">
<generator class="sequence"/>
</collection-id>
<key column="PERSON1"/>
<many-to-many column="PERSON2" class="Person" fetch="join"/>
</idbag
>
ご存知のように <idbag> はエンティティクラスのように人工的な id ジェネレータを持っています。異なる代理キーをそれぞれのコレクションの列に割り当てます。しかし、 Hibernate はある行の代理キーの値を見つけ出す機構を持っていません。
<idbag> を更新するパフォーマンスは通常の <bag> よりも良いことに注目してください。 Hibernate は個々の行を効果的に見つけることができ、 list や map 、 set のように個別にその行を更新、削除できます。
現在の実装では、 native という id 生成戦略を <idbag> コレクションの識別子に対して使えません。
This section covers collection examples.
The following class has a collection of Child instances:
package eg;
import java.util.Set;
public class Parent {
private long id;
private Set children;
public long getId() { return id; }
private void setId(long id) { this.id=id; }
private Set getChildren() { return children; }
private void setChildren(Set children) { this.children=children; }
....
....
}
If each child has, at most, one parent, the most natural mapping is a one-to-many association:
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping
>
これは以下のテーブル定義にマッピングします。
create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
alter table child add constraint childfk0 (parent_id) references parent
もし parent が 要求 されるなら、双方向の一対多関連を使用してください:
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
<many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
</class>
</hibernate-mapping
>
NOT NULL 制約に注意してください。
create table parent ( id bigint not null primary key )
create table child ( id bigint not null
primary key,
name varchar(255),
parent_id bigint not null )
alter table child add constraint childfk0 (parent_id) references parent
あるいは、もしこの関連は単方向であるべきと強く主張するのであれば、 <key> マッピングに NOT NULL 制約を宣言できます:
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping
>
一方で、もし child が複数の parent を持てるならば、多対多関連が妥当です:
<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" table="childset">
<key column="parent_id"/>
<many-to-many class="Child" column="child_id"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping
>
テーブル定義は以下のようになります:
create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255) )
create table childset ( parent_id bigint not null,
child_id bigint not null,
primary key ( parent_id, child_id ) )
alter table childset add constraint childsetfk0 (parent_id) references parent
alter table childset add constraint childsetfk1 (child_id) references childFor more examples and a complete explanation of a parent/child relationship mapping, see 22章例: 親/子供 for more information.
Even more complex association mappings are covered in the next chapter.
関連マッピングはしばしば理解が最も難しいものになります。この章では、基本的な一つ一つのケースについて述べます。単方向のマッピングから始め、それから双方向のケースについて考えていきます。例として、 Person と Address を用います。
関連は、結合テーブルを入れるかかどうかと、多重度によって分類することにします。
null 可能な外部キーは従来型データモデリングの中では良い習慣と見なされていないため、すべての例で not null の外部キーを使用します。これは Hibernate の要件ではありません。 not null 制約を外したとしても、マッピングは問題なく動作します。
単方向多対一関連 は単方向関連の中で最も一般的なものです。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
外部キーの単方向一対一関連 はほとんど同じものです。唯一違うのは、カラムのユニークな制約です。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
主キーの単方向一対一関連 は通常、特別な ID ジェネレータを使います。 (この例では関連の方向が逆になっていることに注意してください)
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property"
>person</param>
</generator>
</id>
<one-to-one name="person" constrained="true"/>
</class
>
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
外部キーの単方向一対多関連 はとても特殊なケースで、あまり推奨されていません。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses">
<key column="personId"
not-null="true"/>
<one-to-many class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key )
create table Address ( addressId bigint not null primary key, personId bigint not null )
このような関連のために結合テーブルを使うことをお薦めします。
結合テーブルを使った単方向一対多関連 はより好ましいです。 unique="true" の指定により、多重度が多対多から一対多に変わったことに注意して下さい。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId not null, addressId bigint not null primary key )
create table Address ( addressId bigint not null primary key )
結合テーブルの単方向多対一関連 は関連が任意であるときに非常に一般的なものです。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
結合テーブルの単方向一対一関連 は、非常に特殊ですが不可能ではありません。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
最後に、 単方向多対多関連 を示します。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key )
双方向多対一関連 は最も一般的な関連です。 (標準的な親子関係です)
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true">
<key column="addressId"/>
<one-to-many class="Person"/>
</set>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
List (または他のインデックス付きのコレクション)を使うなら、外部キーの key カラムを not null に設定し、コレクション側が各要素のインデックスをメンテナンスするように、関連を扱う必要があります (update="false" かつ insert="false" と設定して、反対側を仮想的に inverse にします):
<class name="Person">
<id name="id"/>
...
<many-to-one name="address"
column="addressId"
not-null="true"
insert="false"
update="false"/>
</class>
<class name="Address">
<id name="id"/>
...
<list name="people">
<key column="addressId" not-null="true"/>
<list-index column="peopleIdx"/>
<one-to-many class="Person"/>
</list>
</class
>
If the underlying foreign key column is NOT NULL, it is important that you define not-null="true" on the <key> element of the collection mapping. Do not only declare not-null="true" on a possible nested <column> element, but on the <key> element.
外部キーの双方向一対一関連 は非常に一般的です。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<one-to-one name="person"
property-ref="address"/>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
主キーの双方向一対一関連 は特殊な ID ジェネレータを使います。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<one-to-one name="address"/>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property"
>person</param>
</generator>
</id>
<one-to-one name="person"
constrained="true"/>
</class
>
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
結合テーブルの双方向一対多関連 です。 inverse="true" が関連端、コレクション、結合のいずれかに設定できることに注意してください。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses"
table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
inverse="true"
optional="true">
<key column="addressId"/>
<many-to-one name="person"
column="personId"
not-null="true"/>
</join>
</class
>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
create table Address ( addressId bigint not null primary key )
結合テーブルの双方向一対一関連 は非常に特殊ですが、可能です。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true"
inverse="true">
<key column="addressId"
unique="true"/>
<many-to-one name="person"
column="personId"
not-null="true"
unique="true"/>
</join>
</class
>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
最後に、 双方向多対多関連 を示します。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many