Hibernate.orgCommunity Documentation
3.6.10.Final
製作著作 © 2004 Red Hat, Inc.
February 8, 2012
Working with both Object-Oriented software and Relational Databases can be cumbersome and time consuming. Development costs are significantly higher due to a paradigm mismatch between how data is represented in objects versus relational databases. Hibernate is an Object/Relational Mapping solution for Java environments. The term Object/Relational Mapping refers to the technique of mapping data from an object model representation to a relational data model representation (and visa versa). See http://en.wikipedia.org/wiki/Object-relational_mapping for a good high-level discussion.
While having a strong background in SQL is not required to use Hibernate, having a basic understanding of the concepts can greatly help you understand Hibernate more fully and quickly. Probably the single best background is an understanding of data modeling principles. You might want to consider these resources as a good starting point:
Hibernate not only takes care of the mapping from Java classes to database tables (and from Java data types to SQL data types), but also provides data query and retrieval facilities. It can significantly reduce development time otherwise spent with manual data handling in SQL and JDBC. Hibernate’s design goal is to relieve the developer from 95% of common data persistence-related programming tasks by eliminating the need for manual, hand-crafted data processing using SQL and JDBC. However, unlike many other persistence solutions, Hibernate does not hide the power of SQL from you and guarantees that your investment in relational technology and knowledge is as valid as always.
Hibernate may not be the best solution for data-centric applications that only use stored-procedures to implement the business logic in the database, it is most useful with object-oriented domain models and business logic in the Java-based middle-tier. However, Hibernate can certainly help you to remove or encapsulate vendor-specific SQL code and will help with the common task of result set translation from a tabular representation to a graph of objects.
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 など)を検索する上で興味深いリソースになります。
There are a number of ways to become involved in the Hibernate community, including
Trying stuff out and reporting bugs. See http://hibernate.org/issuetracker.html details.
Trying your hand at fixing some bugs or implementing enhancements. Again, see http://hibernate.org/issuetracker.html details.
http://hibernate.org/community.html list a few ways to engage in the community.
There are forums for users to ask questions and receive help from the community.
There are also IRC channels for both user and developer discussions.
Helping improve or translate this documentation. Contact us on the developer mailing list if you have interest.
Evangelizing Hibernate within your organization.
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://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.tutorial.domain">
[...]
</hibernate-mapping
>
Hibernate DTD が非常に洗練されていることに注目してください。この DTD は、エディタや IDE での XML マッピング要素と属性のオートコンプリーション機能に利用できます。また DTD ファイルをテキストエディタで開けてみてください。というのも、すべての要素と属性を概観し、コメントやデフォルトの値を見るには一番簡単な方法だからです。 Hibernate は、 web から DTD ファイルをロードせずに、まずアプリケーションのクラスパスからこれを探し出そうとすることに注意してください。 DTD ファイルは Hibernate ディストリビューションの src/ ディレクトリと同様、hibernate3.jar にも含まれています。
以降の例ではコードを短くするために DTD 宣言を省略します。当然ですがこれはオプションではありません。
2つの hibernate-mapping タグの間に class 要素を含めてください。すべての永続エンティティクラス(念を押しますが、ファーストクラスのエンティティではない依存クラスというものが後ほど登場します)は SQL データベース内のテーブルへのこのようなマッピングを必要とします。
<hibernate-mapping package="org.hibernate.tutorial.domain">
<class name="Event" table="EVENTS">
</class>
</hibernate-mapping>
これまで私たちは、 Event クラスのオブジェクトを EVENTS テーブルに対して、どのように永続化したりロードしたりするのかを Hibernate に教えてきました。そして個々のインスタンスはテーブルの行として表現されます。それでは引き続きテーブルの主キーに対するユニークな識別子プロパティをマッピングしていきます。さらに、この識別子の扱いに気を使いたくなかったのと同様に、代理の主キーカラムに対する Hibernate の識別子生成戦略を設定します。
<hibernate-mapping package="org.hibernate.tutorial.domain">
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
</class>
</hibernate-mapping>
The id element is the declaration of the identifier property. The name="id" mapping attribute declares the name of the JavaBean property and tells Hibernate to use the getId() and setId() methods to access the property. The column attribute tells Hibernate which column of the EVENTS table holds the primary key value.
The nested generator element specifies the identifier generation strategy (aka how are identifier values generated?). In this case we choose native, which offers a level of portability depending on the configured database dialect. Hibernate supports database generated, globally unique, as well as application assigned, identifiers. Identifier value generation is also one of Hibernate's many extension points and you can plugin in your own strategy.
native is no longer consider the best strategy in terms of portability. for further discussion, see 「Identifier generation」
最後にクラスの永続プロパティの宣言をマッピングファイルに含めます。デフォルトでは、クラスのプロパティは永続と見なされません:
<hibernate-mapping package="org.hibernate.tutorial.domain">
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
</class>
</hibernate-mapping>
id 要素の場合と同様に、 property 要素の name 属性で、どのゲッターとセッターメソッドを使うべきかを Hibernate に教えます。この例では、 Hibernate は getDate()/setDate() と getTitle()/setTitle() を探します。
なぜ date プロパティのマッピングには column 属性があり、 title プロパティにはないのでしょうか? column 属性がなければ、 Hibernate はデフォルトでプロパティ名をカラム名として使います。これは title では上手くいきます。しかし date は、ほとんどのデータベースで予約語なので、違う名前でマッピングした方がよいのです。
次に興味深いのは title マッピングが type 属性をも欠いている点です。マッピングファイルで宣言して使う type は、おわかりかもしれませんが Java のデータ型ではありません。 SQL データベースの型でもありません。これは Hibernateマッピング型 と呼ばれる、 Java から SQL データの型へまたは SQL から Java データ型へ翻訳するコンバータです。繰り返しになりますが、 Hibernate は type 属性がマッピングファイル内になければ、正しいコンバージョンとマッピング型を自分で解決しようとします。 (Javaクラスのリフレクションを使った)この自動検知は、場合によってはあなたが期待または必要とするデフォルト値にならないかもしれません。 date プロパティの場合がそうでした。 Hibernate はこの( java.util.Date の)プロパティを SQL の date , timestamp , time のうち、どのカラムにマッピングするべきなのかわかりません。 timestamp コンバータでプロパティをマッピングすることにより、完全な日時を保存します。
Hibernate makes this mapping type determination using reflection when the mapping files are processed. This can take time and resources, so if startup performance is important you should consider explicitly defining the type to use.
Save this mapping file as src/main/resources/org/hibernate/tutorial/domain/Event.hbm.xml.
At this point, you should have the persistent class and its mapping file in place. It is now time to configure Hibernate. First let's set up HSQLDB to run in "server mode"
We do this do that the data remains between runs.
We will utilize the Maven exec plugin to launch the HSQLDB server by running: mvn exec:java -Dexec.mainClass="org.hsqldb.Server" -Dexec.args="-database.0 file:target/data/tutorial" You will see it start up and bind to a TCP/IP socket; this is where our application will connect later. If you want to start with a fresh database during this tutorial, shutdown HSQLDB, delete all files in the target/data directory, and start HSQLDB again.
Hibernate will be connecting to the database on behalf of your application, so it needs to know how to obtain connections. For this tutorial we will be using a standalone connection pool (as opposed to a javax.sql.DataSource). Hibernate comes with support for two third-party open source JDBC connection pools: c3p0 and proxool. However, we will be using the Hibernate built-in connection pool for this tutorial.
The built-in Hibernate connection pool is in no way intended for production use. It lacks several features found on any decent connection pool.
Hibernate の設定では、単純な hibernate.properties ファイル、それより少し洗練されている hibernate.cfg.xml ファイル、または完全にプログラム上でセットアップする方法が利用できます。ほとんどのユーザーが好むのは XML 設定ファイルです:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class"
>org.hsqldb.jdbcDriver</property>
<property name="connection.url"
>jdbc:hsqldb:hsql://localhost</property>
<property name="connection.username"
>sa</property>
<property name="connection.password"
></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size"
>1</property>
<!-- SQL dialect -->
<property name="dialect"
>org.hibernate.dialect.HSQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class"
>thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class"
>org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql"
>true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto"
>update</property>
<mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
</session-factory>
</hibernate-configuration
>
この XML の設定が異なる DTD を使うことに注意してください。
特定のデータベースを受け持つグローバルファクトリである Hibernate の SessionFactory を設定します。もし複数のデータベースがある場合には、 (スタートアップを簡単にするため)通常いくつかの設定ファイル内で、いくつかの <session-factory> を使う設定にしてください。
最初の4つの property 要素は JDBC コネクションに必要な設定を含んでいます。 dialect という名前の property 要素は、 Hibernate が生成する特定の SQL 方言を指定します。
In most cases, Hibernate is able to properly determine which dialect to use. See 「Dialect resolution」 for more information.
永続的なコンテキストに対する Hibernate のセッションの自動管理は、後の例ですぐにわかるように、役に立つことでしょう。 hbm2ddl.auto オプションはデータベーススキーマの自動生成を on にします。これは直接データベースに対して生成されます。当然(config オプションを削除して) off にしたり、 SchemaExport という Ant タスクの助けを借りてファイルにリダイレクトしたりできます。最後に永続クラスのためのマッピングファイルを設定に追加します。
Save this file as hibernate.cfg.xml into the src/main/resources directory.
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 13章Transactions and Concurrency for more information about transaction handling and demarcation. The previous example also skipped any error handling and rollback.
To run this, we will make use of the Maven exec plugin to call our class with the necessary classpath setup: mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="store"
You may need to perform mvn compile first.
コンパイルすると、 Hibernate がスタートし、設定によりますが、多くのログ出力があるはずです。その最後には以下の行があるでしょう:
[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)
This is the INSERT executed by Hibernate.
To list stored events an option is added to the main method:
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
else if (args[0].equals("list")) {
List events = mgr.listEvents();
for (int i = 0; i < events.size(); i++) {
Event theEvent = (Event) events.get(i);
System.out.println(
"Event: " + theEvent.getTitle() + " Time: " + theEvent.getDate()
);
}
}
新しい listEvents()メソッド も追加します。
private List listEvents() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
List result = session.createQuery("from Event").list();
session.getTransaction().commit();
return result;
}
Here, we are using a Hibernate Query Language (HQL) query to load all existing Event objects from the database. Hibernate will generate the appropriate SQL, send it to the database and populate Event objects with the data. You can create more complex queries with HQL. See 16章HQL: Hibernate クエリ言語 for more information.
Now we can call our new functionality, again using the Maven exec plugin: mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="list"
永続エンティティクラスをテーブルにマッピングしました。さらにこの上にいくつかのクラスの関連を追加しましょう。まず初めにアプリケーションに人々を追加し、彼らが参加するイベントのリストを格納します。
最初の Person クラスは単純です:
package org.hibernate.tutorial.domain;
public class Person {
private Long id;
private int age;
private String firstname;
private String lastname;
public Person() {}
// Accessor methods for all properties, private setter for 'id'
}
Save this to a file named src/main/java/org/hibernate/tutorial/domain/Person.java
Next, create the new mapping file as src/main/resources/org/hibernate/tutorial/domain/Person.hbm.xml
<hibernate-mapping package="org.hibernate.tutorial.domain">
<class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>
</class>
</hibernate-mapping>
最後に Hibernate の設定に新しいマッピングを追加してください:
<mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
<mapping resource="org/hibernate/tutorial/domain/Person.hbm.xml"/>
それではこれら2つのエンティティ間の関連を作成します。人々がイベントに参加でき、イベントが参加者を持つのは明らかです。扱わなければならない設計の問題は、方向、多重度、コレクションの振る舞いです。
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="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 アーキテクチャの(非常に)高いレベルからのビュー:

Unfortunately we cannot provide a detailed view of all possible runtime architectures. Hibernate is sufficiently flexible to be used in a number of ways in many, many architectures. We will, however, illustrate 2 specifically since they are extremes.
The "minimal" architecture has the application manage its own JDBC connections and provide those connections to Hibernate; additionally the application manages transactions for itself. This approach uses a minimal subset of Hibernate APIs.

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

Here are quick discussions about some of the API objects depicted in the preceding diagrams (you will see them again in more detail in later chapters).
org.hibernate.SessionFactory)A thread-safe, immutable cache of compiled mappings for a single database. A factory for org.hibernate.Session instances. A client of org.hibernate.connection.ConnectionProvider. Optionally maintains a 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. Wraps a JDBC java.sql.Connection. Factory for org.hibernate.Transaction. Maintains a first level cache of persistent the application's persistent objects and collections; this cache is used when navigating the object graph or looking up objects by identifier.
Short-lived, single threaded objects containing persistent state and business function. These can be ordinary JavaBeans/POJOs. They are associated with exactly one org.hibernate.Session. Once the org.hibernate.Session is closed, they will be detached and free to use in any application layer (for example, directly as data transfer objects to and from presentation). 11章オブジェクトを扱う discusses transient, persistent and detached object states.
Instances of persistent classes that are not currently associated with a org.hibernate.Session. They may have been instantiated by the application and not yet persisted, or they may have been instantiated by a closed org.hibernate.Session. 11章オブジェクトを扱う discusses transient, persistent and detached object states.
org.hibernate.Transaction)(Optional) A single-threaded, short-lived object used by the application to specify atomic units of work. It abstracts the application from the underlying JDBC, JTA or CORBA transaction. A org.hibernate.Session might span several org.hibernate.Transactions in some cases. However, transaction demarcation, either using the underlying API or org.hibernate.Transaction, is never optional.
org.hibernate.connection.ConnectionProvider)(Optional) A factory for, and pool of, JDBC connections. It abstracts the application from underlying javax.sql.DataSource or java.sql.DriverManager. It is not exposed to application, but it can be extended and/or implemented by the developer.
org.hibernate.TransactionFactory)(Optional) A factory for org.hibernate.Transaction instances. It is not exposed to the application, but it can be extended and/or implemented by the developer.
Hibernate は、永続層の振る舞いをカスタマイズするために、多くのオプション拡張インタフェースを用意しています。詳細は API ドキュメントを参照してください。
JMX は Java コンポーネント管理の J2EE 標準です。 JMX 標準サービスを通して、 Hibernate は管理されます。ディストリビューションの中に org.hibernate.jmx.HibernateService という MBean 実装を用意しています。
Another feature available as a JMX service is runtime Hibernate statistics. See 「Hibernate 統計」 for more information.
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 13章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 | データベースのユーザー |
| hibernate.connection.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 | データソースの JNDI 名 |
| 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 です。 例 |
| hibernate.id.new_generator_mappings | Setting is relevant when using @GeneratedValue. It indicates whether or not the new IdentifierGenerator implementations are used for javax.persistence.GenerationType.AUTO, javax.persistence.GenerationType.TABLE and javax.persistence.GenerationType.SEQUENCE. Default to false to keep backward compatibility. 例 |
We recommend all new projects which make use of to use @GeneratedValue to also set hibernate.id.new_generator_mappings=true as the new generators are more efficient and closer to the JPA 2 specification semantic. However they are not backward compatible with existing databases (if a sequence or a table is used for id generation).
表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 | 二次キャッシュに格納するデータを、人が理解しやすいフォーマットにします。 例 |
hibernate.cache.default_cache_concurrency_strategy | Setting used to give the name of the default org.hibernate.annotations.CacheConcurrencyStrategy to use when either @Cacheable or @Cache is used. @Cache(strategy="..") is used to override this default. |
表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.hbm2ddl.import_files | Comma-separated names of the optional files containing SQL DML statements executed during the File order matters, the statements of a give file are executed before the statements of the following files. These statements are only executed if the schema is created ie if e.g. |
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 |
| MySQL5 | org.hibernate.dialect.MySQL5Dialect |
| MySQL5 with InnoDB | org.hibernate.dialect.MySQL5InnoDBDialect |
| MySQL with MyISAM | org.hibernate.dialect.MySQLMyISAMDialect |
| Oracle (いずれのバージョンでも) | org.hibernate.dialect.OracleDialect |
| Oracle 9i | org.hibernate.dialect.Oracle9iDialect |
| Oracle 10g | org.hibernate.dialect.Oracle10gDialect |
| Oracle 11g | org.hibernate.dialect.Oracle10gDialect |
| Sybase | org.hibernate.dialect.SybaseASE15Dialect |
| Sybase Anywhere | org.hibernate.dialect.SybaseAnywhereDialect |
| Microsoft SQL Server 2000 | org.hibernate.dialect.SQLServerDialect |
| Microsoft SQL Server 2005 | org.hibernate.dialect.SQLServer2005Dialect |
| Microsoft SQL Server 2008 | org.hibernate.dialect.SQLServer2008Dialect |
| SAP DB | org.hibernate.dialect.SAPDBDialect |
| Informix | org.hibernate.dialect.InformixDialect |
| HypersonicSQL | org.hibernate.dialect.HSQLDialect |
| H2 Database | org.hibernate.dialect.H2Dialect |
| 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 は組み込みの戦略です。これはいくつかのアプリケーションにとって有用な開始点となるかもしれません。
You can configure the persister implementation used to persist your entities and collections:
by default, Hibernate uses persisters that make sense in a relational model and follow Java Persistence's specification
you can define a PersisterClassProvider implementation that provides the persister class used of a given entity or collection
finally, you can override them on a per entity and collection basis in the mapping using @Persister or its XML equivalent
The latter in the list the higher in priority.
You can pass the PersisterClassProvider instance to the Configuration object.
SessionFactory sf = new Configuration()
.setPersisterClassProvider(customPersisterClassProvider)
.addAnnotatedClass(Order.class)
.buildSessionFactory();
The persister class provider methods, when returning a non null persister class, override the default Hibernate persisters. The entity name or the collection role are passed to the methods. It is a nice way to centralize the overriding logic of the persisters instead of spreading them on each entity or collection mapping.
もう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://www.hibernate.org/dtd/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 AS |
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 |
org.hibernate.transaction.JBossTSStandaloneTransactionManagerLookup | JBoss TS used standalone (ie. outside JBoss AS and a JNDI environment generally). Known to work for org.jboss.jbossts:jbossjta:4.11.0.Final |
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 を取得します。
It is recommended that you bind the SessionFactory to JNDI in a managed environment and use a static singleton otherwise. To shield your application code from these details, we also recommend to hide the actual lookup code for a SessionFactory in a helper class, such as HibernateUtil.getSessionFactory(). Note that such a class is also a convenient way to startup Hibernate—see chapter 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 デプロイメントのより多くの情報があります。
Persistent classes are classes in an application that implement the entities of the business problem (e.g. Customer and Order in an E-commerce application). The term "persistent" here means that the classes are able to be persisted, not that they are in the persistent state (see 「Hibernate におけるオブジェクトの状態」 for discussion).
Hibernate works best if these classes follow some simple rules, also known as the Plain Old Java Object (POJO) programming model. However, none of these rules are hard requirements. Indeed, Hibernate assumes very little about the nature of your persistent objects. You can express a domain model in other ways (using trees of java.util.Map instances, for example).
例4.1 Simple POJO representing a cat
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 has a no-argument constructor. All persistent classes must have a default constructor (which can be non-public) so that Hibernate can instantiate them using . It is recommended that this constructor be defined with at least package visibility in order for runtime proxy generation to work properly. java.lang.reflect.Constructor.newInstance()
Historically this was considered option. While still not (yet) enforced, this should be considered a deprecated feature as it will be completely required to provide a identifier property in an upcoming release.
Cat has a property named id. This property maps to the primary key column(s) of the underlying database table. The type of the identifier property can be any "basic" type (see ???). See 「複合識別子としてのコンポーネント」 for information on mapping composite (multi-column) identifiers.
Identifiers do not necessarily need to identify column(s) in the database physically defined as a primary key. They should just identify columns that can be used to uniquely identify rows in the underlying table.
永続クラスには、一貫した名前の識別子プロパティを定義することをお勧めします。さらに null 値を取れる(つまりプリミティブではない)型を使った方がよいでしょう。
A central feature of Hibernate, proxies (lazy loading), depends upon the persistent class being either non-final, or the implementation of an interface that declares all public methods. You can persist final classes that do not implement an interface with Hibernate; you will not, however, be able to use proxies for lazy association fetching which will ultimately limit your options for performance tuning. To persist a final class which does not implement a "full" interface you must disable proxy generation. See 例4.2「Disabling proxies in hbm.xml」 and 例4.3「Disabling proxies in annotations」.
If the final class does implement a proper interface, you could alternatively tell Hibernate to use the interface instead when generating the proxies. See 例4.4「Proxying an interface in hbm.xml」 and 例4.5「Proxying an interface in annotations」.
例4.5 Proxying an interface in annotations
@Entity @Proxy(proxyClass=ICat.class) public class Cat implements ICat { ... }
You should also avoid declaring public final methods as this will again limit the ability to generate proxies from this class. If you want to use a class with public final methods, you must explicitly disable proxying. Again, see 例4.2「Disabling proxies in hbm.xml」 and 例4.3「Disabling proxies in annotations」.
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.
Properties need not be declared public. Hibernate can persist a property declared with package, protected or private visibility as well.
サブクラスも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 20章XML マッピング.
org.hibernate.tuple.Tuplizer and its sub-interfaces are responsible for managing a particular representation of a piece of data given that representation's org.hibernate.EntityMode. If a given piece of data is thought of as a data structure, then a tuplizer is the thing that knows how to create such a data structure, how to extract values from such a data structure and how to inject values into such a data structure. For example, for the POJO entity mode, the corresponding tuplizer knows how create the POJO through its constructor. It also knows how to access the POJO properties using the defined property accessors.
There are two (high-level) types of Tuplizers:
org.hibernate.tuple.entity.EntityTuplizer which is responsible for managing the above mentioned contracts in regards to entities
org.hibernate.tuple.component.ComponentTuplizer which does the same for components
Users can also plug in their own tuplizers. Perhaps you require that java.util.Map implementation other than java.util.HashMap be used while in the dynamic-map entity-mode. Or perhaps you need to define a different proxy generation strategy than the one used by default. Both would be achieved by defining a custom tuplizer implementation. Tuplizer definitions are attached to the entity or component mapping they are meant to manage. Going back to the example of our Customer entity, 例4.6「Specify custom tuplizers in annotations」 shows how to specify a custom org.hibernate.tuple.entity.EntityTuplizer using annotations while 例4.7「Specify custom tuplizers in hbm.xml」 shows how to do the same in hbm.xml
例4.6 Specify custom tuplizers in annotations
@Entity
@Tuplizer(impl = DynamicEntityTuplizer.class)
public interface Cuisine {
@Id
@GeneratedValue
public Long getId();
public void setId(Long id);
public String getName();
public void setName(String name);
@Tuplizer(impl = DynamicComponentTuplizer.class)
public Country getCountry();
public void setCountry(Country country);
}
例4.7 Specify custom tuplizers in hbm.xml
<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>
org.hibernate.EntityNameResolver 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 implementations as is
*/
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 (see 「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.
Object/relational mappings can be defined in three approaches:
using Java 5 annotations (via the Java Persistence 2 annotations)
using JPA 2 XML deployment descriptors (described in chapter XXX)
using the Hibernate legacy XML files approach known as hbm.xml
Annotations are split in two categories, the logical mapping annotations (describing the object model, the association between two entities etc.) and the physical mapping annotations (describing the physical schema, tables, columns, indexes, etc). We will mix annotations from both categories in the following code examples.
JPA annotations are in the javax.persistence.* package. Hibernate specific extensions are in org.hibernate.annotations.*. You favorite IDE can auto-complete annotations and their attributes for you (even without a specific "JPA" plugin, since JPA annotations are plain Java 5 annotations).
Here is an example of mapping
package eg;
@Entity
@Table(name="cats") @Inheritance(strategy=SINGLE_TABLE)
@DiscriminatorValue("C") @DiscriminatorColumn(name="subclass", discriminatorType=CHAR)
public class Cat {
@Id @GeneratedValue
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
public BigDecimal getWeight() { return weight; }
public void setWeight(BigDecimal weight) { this.weight = weight; }
private BigDecimal weight;
@Temporal(DATE) @NotNull @Column(updatable=false)
public Date getBirthdate() { return birthdate; }
public void setBirthdate(Date birthdate) { this.birthdate = birthdate; }
private Date birthdate;
@org.hibernate.annotations.Type(type="eg.types.ColorUserType")
@NotNull @Column(updatable=false)
public ColorType getColor() { return color; }
public void setColor(ColorType color) { this.color = color; }
private ColorType color;
@NotNull @Column(updatable=false)
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
private String sex;
@NotNull @Column(updatable=false)
public Integer getLitterId() { return litterId; }
public void setLitterId(Integer litterId) { this.litterId = litterId; }
private Integer litterId;
@ManyToOne @JoinColumn(name="mother_id", updatable=false)
public Cat getMother() { return mother; }
public void setMother(Cat mother) { this.mother = mother; }
private Cat mother;
@OneToMany(mappedBy="mother") @OrderBy("litterId")
public Set<Cat> getKittens() { return kittens; }
public void setKittens(Set<Cat> kittens) { this.kittens = kittens; }
private Set<Cat> kittens = new HashSet<Cat>();
}
@Entity @DiscriminatorValue("D")
public class DomesticCat extends Cat {
public String getName() { return name; }
public void setName(String name) { this.name = name }
private String name;
}
@Entity
public class Dog { ... }
The legacy hbm.xml approach uses an XML schema designed to be readable and hand-editable. The mapping language is Java-centric, meaning that mappings are constructed around persistent class declarations and not table declarations.
多くの Hibernate ユーザーは XML マッピングの記述を手作業で行いますが、 XDoclet, Middlegen, AndroMDA というようなマッピングドキュメントを生成するツールがいくつか存在することを覚えておいてください。
サンプルのマッピングから始めましょう:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="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>
We will now discuss the concepts of the mapping documents (both annotations and XML). We will only describe, however, the document elements and attributes that are used by Hibernate at runtime. The mapping document also contains some extra optional attributes and elements that affect the database schemas exported by the schema export tool (for example, the not-null attribute).
An entity is a regular Java object (aka POJO) which will be persisted by Hibernate.
To mark an object as an entity in annotations, use the @Entity annotation.
@Entity
public class Flight implements Serializable {
Long id;
@Id
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
}
That's pretty much it, the rest is optional. There are however any options to tweak your entity mapping, let's explore them.
@Table lets you define the table the entity will be persisted into. If undefined, the table name is the unqualified class name of the entity. You can also optionally define the catalog, the schema as well as unique constraints on the table.
@Entity
@Table(name="TBL_FLIGHT",
schema="AIR_COMMAND",
uniqueConstraints=
@UniqueConstraint(
name="flight_number",
columnNames={"comp_prefix", "flight_number"} ) )
public class Flight implements Serializable {
@Column(name="comp_prefix")
public String getCompagnyPrefix() { return companyPrefix; }
@Column(name="flight_number")
public String getNumber() { return number; }
}
The constraint name is optional (generated if left undefined). The column names composing the constraint correspond to the column names as defined before the Hibernate NamingStrategy is applied.
@Entity.name lets you define the shortcut name of the entity you can used in JP-QL and HQL queries. It defaults to the unqualified class name of the class.
Hibernate goes beyond the JPA specification and provide additional configurations. Some of them are hosted on @org.hibernate.annotations.Entity:
dynamicInsert / dynamicUpdate (defaults to false): specifies that INSERT / UPDATE SQL should be generated at runtime and contain only the columns whose values are not null. The dynamic-update and dynamic-insert settings are not inherited by subclasses. Although these settings can increase performance in some cases, they can actually decrease performance in others.
selectBeforeUpdate (defaults to false): specifies that Hibernate should never perform an SQL UPDATE unless it is certain that an object is actually modified. Only when a transient object has been associated with a new session using update(), will Hibernate perform an extra SQL SELECT to determine if an UPDATE is actually required. Use of select-before-update will usually decrease performance. It is useful to prevent a database update trigger being called unnecessarily if you reattach a graph of detached instances to a Session.
polymorphisms (defaults to IMPLICIT): determines whether implicit or explicit query polymorphisms is used. Implicit polymorphisms means that instances of the class will be returned by a query that names any superclass or implemented interface or class, and that instances of any subclass of the class will be returned by a query that names the class itself. Explicit polymorphisms means that class instances will be returned only by queries that explicitly name that class. Queries that name the class will return only instances of subclasses mapped. For most purposes, the default polymorphisms=IMPLICIT is appropriate. Explicit polymorphisms is useful when two different classes are mapped to the same table This allows a "lightweight" class that contains a subset of the table columns.
persister: specifies a custom ClassPersister. The persister attribute lets you customize the persistence strategy used for the class. You can, for example, specify your own subclass of org.hibernate.persister.EntityPersister, or you can even provide a completely new implementation of the interface org.hibernate.persister.ClassPersister that implements, for example, persistence via stored procedure calls, serialization to flat files or LDAP. See org.hibernate.test.CustomPersister for a simple example of "persistence" to a Hashtable.
optimisticLock (defaults to VERSION): determines the optimistic locking strategy. If you enable dynamicUpdate, you will have a choice of optimistic locking strategies:
version バージョン/タイムスタンプカラムをチェックします。
all すべてのカラムをチェックします。
dirty 変更したカラムをチェックし、同時更新できるようにします。
none 楽観ロックを使用しません。
Hibernate で楽観的ロック戦略を使うなら、バージョン/タイムスタンプカラムを使うことを 非常に 強くお勧めします。楽観的ロックはパフォーマンスの観点からも最適であり、さらに分離インスタンスへの修正 (つまり Session.marge() が使われるとき) を正確に扱うことのできる唯一の戦略でもあります。
Be sure to import @javax.persistence.Entity to mark a class as an entity. It's a common mistake to import @org.hibernate.annotations.Entity by accident.
Some entities are not mutable. They cannot be updated or deleted by the application. This allows Hibernate to make some minor performance optimizations.. Use the @Immutable annotation.
You can also alter how Hibernate deals with lazy initialization for this class. On @Proxy, use lazy=false to disable lazy fetching (not recommended). You can also specify an interface to use for lazy initializing proxies (defaults to the class itself): use proxyClass on @Proxy. Hibernate will initially return proxies (Javassist or CGLIB) that implement the named interface. The persistent object will load when a method of the proxy is invoked. See "Initializing collections and proxies" below.
@BatchSize specifies a "batch size" for fetching instances of this class by identifier. Not yet loaded instances are loaded batch-size at a time (default 1).
You can specific an arbitrary SQL WHERE condition to be used when retrieving objects of this class. Use @Where for that.
In the same vein, @Check lets you define an SQL expression used to generate a multi-row check constraint for automatic schema generation.
There is no difference between a view and a base table for a Hibernate mapping. This is transparent at the database level, although some DBMS do not support views properly, especially with updates. Sometimes you want to use a view, but you cannot create one in the database (i.e. with a legacy schema). In this case, you can map an immutable and read-only entity to a given SQL subselect expression using @org.hibernate.annotations.Subselect:
@Entity
@Subselect("select item.name, max(bid.amount), count(*) "
+ "from item "
+ "join bid on bid.item_id = item.id "
+ "group by item.name")
@Synchronize( {"item", "bid"} ) //tables impacted
public class Summary {
@Id
public String getId() { return id; }
...
}
テーブルをこのエンティティと同期するように定義してください。オートフラッシュが確実に起こるように、また導出エンティティに対するクエリが古いデータを返さないようにするためです。 <subselect> は属性とネストしたマッピング属性のどちらでも利用できます。
We will now explore the same options using the hbm.xml structure. You can declare a persistent class using the class element. For example:
<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 を使ってクラス名を指定してください。
Here is how to do a virtual view (subselect) in XML:
<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>
The <subselect> is available both as an attribute and a nested mapping element.
Mapped classes must declare the primary key column of the database table. Most classes will also have a JavaBeans-style property holding the unique identifier of an instance.
Mark the identifier property with @Id.
@Entity
public class Person {
@Id Integer getId() { ... }
...
}
In hbm.xml, use the <id> element which defines the mapping from that property to the primary key column.
<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 属性がなければ、クラスには識別子プロパティがないものとみなされます。
The unsaved-value attribute is almost never needed in Hibernate3 and indeed has no corresponding element in annotations.
You can also declare the identifier as a composite identifier. This allows access to legacy data with composite keys. Its use is strongly discouraged for anything else.
You can define a composite primary key through several syntaxes:
use a component type to represent the identifier and map it as a property in the entity: you then annotated the property as @EmbeddedId. The component type has to be Serializable.
map multiple properties as @Id properties: the identifier type is then the entity class itself and needs to be Serializable. This approach is unfortunately not standard and only supported by Hibernate.
map multiple properties as @Id properties and declare an external class to be the identifier type. This class, which needs to be Serializable, is declared on the entity via the @IdClass annotation. The identifier type must contain the same properties as the identifier properties of the entity: each property name must be the same, its type must be the same as well if the entity property is of a basic type, its type must be the type of the primary key of the associated entity if the entity property is an association (either a @OneToOne or a @ManyToOne).
As you can see the last case is far from obvious. It has been inherited from the dark ages of EJB 2 for backward compatibilities and we recommend you not to use it (for simplicity sake).
Let's explore all three cases using examples.
Here is a simple example of @EmbeddedId.
@Entity
class User {
@EmbeddedId
@AttributeOverride(name="firstName", column=@Column(name="fld_firstname")
UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
You can notice that the UserId class is serializable. To override the column mapping, use @AttributeOverride.
An embedded id can itself contains the primary key of an associated entity.
@Entity
class Customer {
@EmbeddedId CustomerId id;
boolean preferredCustomer;
@MapsId("userId")
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
@OneToOne User user;
}
@Embeddable
class CustomerId implements Serializable {
UserId userId;
String customerNumber;
//implements equals and hashCode
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
//implements equals and hashCode
}
In the embedded id object, the association is represented as the identifier of the associated entity. But you can link its value to a regular association in the entity via the @MapsId annotation. The @MapsId value correspond to the property name of the embedded id object containing the associated entity's identifier. In the database, it means that the Customer.user and the CustomerId.userId properties share the same underlying column (user_fk in this case).
The component type used as identifier must implement equals() and hashCode().
In practice, your code only sets the Customer.user property and the user id value is copied by Hibernate into the CustomerId.userId property.
The id value can be copied as late as flush time, don't rely on it until after flush time.
While not supported in JPA, Hibernate lets you place your association directly in the embedded id component (instead of having to use the @MapsId annotation).
@Entity
class Customer {
@EmbeddedId CustomerId id;
boolean preferredCustomer;
}
@Embeddable
class CustomerId implements Serializable {
@OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
String customerNumber;
//implements equals and hashCode
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
//implements equals and hashCode
}
Let's now rewrite these examples using the hbm.xml syntax.
<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>
First a simple example:
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName" column="fld_firstname"/>
<key-property name="lastName"/>
</composite-id>
</class>
Then an example showing how an association can be mapped.
<class name="Customer">
<composite-id name="id" class="CustomerId">
<key-property name="firstName" column="userfirstname_fk"/>
<key-property name="lastName" column="userfirstname_fk"/>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
<many-to-one name="user">
<column name="userfirstname_fk" updatable="false" insertable="false"/>
<column name="userlastname_fk" updatable="false" insertable="false"/>
</many-to-one>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
Notice a few things in the previous example:
the order of the properties (and column) matters. It must be the same between the association and the primary key of the associated entity
the many to one uses the same columns as the primary key and thus must be marked as read only (insertable and updatable to false).
unlike with @MapsId, the id value of the associated entity is not transparently copied, check the foreign id generator for more information.
The last example shows how to map association directly in the embedded id component.
<class name="Customer">
<composite-id name="id" class="CustomerId">
<key-many-to-one name="user">
<column name="userfirstname_fk"/>
<column name="userlastname_fk"/>
</key-many-to-one>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
This is the recommended approach to map composite identifier. The following options should not be considered unless some constraint are present.
Another, arguably more natural, approach is to place @Id on multiple properties of your entity. This approach is only supported by Hibernate (not JPA compliant) but does not require an extra embeddable component.
@Entity
class Customer implements Serializable {
@Id @OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
@Id String customerNumber;
boolean preferredCustomer;
//implements equals and hashCode
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
//implements equals and hashCode
}
In this case Customer is its own identifier representation: it must implement Serializable and must implement equals() and hashCode().
In hbm.xml, the same mapping is:
<class name="Customer">
<composite-id>
<key-many-to-one name="user">
<column name="userfirstname_fk"/>
<column name="userlastname_fk"/>
</key-many-to-one>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
@IdClass on an entity points to the class (component) representing the identifier of the class. The properties marked @Id on the entity must have their corresponding property on the @IdClass. The return type of search twin property must be either identical for basic properties or must correspond to the identifier class of the associated entity for an association.
This approach is inherited from the EJB 2 days and we recommend against its use. But, after all it's your application and Hibernate supports it.
@Entity
@IdClass(CustomerId.class)
class Customer implements Serializable {
@Id @OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
@Id String customerNumber;
boolean preferredCustomer;
}
class CustomerId implements Serializable {
UserId user;
String customerNumber;
//implements equals and hashCode
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
//implements equals and hashCode
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
//implements equals and hashCode
}
Customer and CustomerId do have the same properties customerNumber as well as user. CustomerId must be Serializable and implement equals() and hashCode().
While not JPA standard, Hibernate let's you declare the vanilla associated property in the @IdClass.
@Entity
@IdClass(CustomerId.class)
class Customer implements Serializable {
@Id @OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
@Id String customerNumber;
boolean preferredCustomer;
}
class CustomerId implements Serializable {
@OneToOne User user;
String customerNumber;
//implements equals and hashCode
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
//implements equals and hashCode
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
This feature is of limited interest though as you are likely to have chosen the @IdClass approach to stay JPA compliant or you have a quite twisted mind.
Here are the equivalent on hbm.xml files:
<class name="Customer">
<composite-id class="CustomerId" mapped="true">
<key-many-to-one name="user">
<column name="userfirstname_fk"/>
<column name="userlastname_fk"/>
</key-many-to-one>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
Hibernate can generate and populate identifier values for you automatically. This is the recommended approach over "business" or "natural" id (especially composite ids).
Hibernate offers various generation strategies, let's explore the most common ones first that happens to be standardized by JPA:
IDENTITY: supports identity columns in DB2, MySQL, MS SQL Server, Sybase and HypersonicSQL. The returned identifier is of type long, short or int.
SEQUENCE (called seqhilo in Hibernate): uses a hi/lo algorithm to efficiently generate identifiers of type long, short or int, given a named database sequence.
TABLE (called MultipleHiLoPerTableGenerator in Hibernate) : uses a hi/lo algorithm to efficiently generate identifiers of type long, short or int, given a table and column as a source of hi values. The hi/lo algorithm generates identifiers that are unique only for a particular database.
AUTO: selects IDENTITY, SEQUENCE or TABLE depending upon the capabilities of the underlying database.
We recommend all new projects to use the new enhanced identifier generators. They are deactivated by default for entities using annotations but can be activated using hibernate.id.new_generator_mappings=true. These new generators are more efficient and closer to the JPA 2 specification semantic.
However they are not backward compatible with existing Hibernate based application (if a sequence or a table is used for id generation). See XXXXXXX ??? for more information on how to activate them.
To mark an id property as generated, use the @GeneratedValue annotation. You can specify the strategy used (default to AUTO) by setting strategy.
@Entity
public class Customer {
@Id @GeneratedValue
Integer getId() { ... };
}
@Entity
public class Invoice {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
Integer getId() { ... };
}
SEQUENCE and TABLE require additional configurations that you can set using @SequenceGenerator and @TableGenerator:
name: name of the generator
table / sequenceName: name of the table or the sequence (defaulting respectively to hibernate_sequences and hibernate_sequence)
catalog / schema:
initialValue: the value from which the id is to start generating
allocationSize: the amount to increment by when allocating id numbers from the generator
In addition, the TABLE strategy also let you customize:
pkColumnName: the column name containing the entity identifier
valueColumnName: the column name containing the identifier value
pkColumnValue: the entity identifier
uniqueConstraints: any potential column constraint on the table containing the ids
To link a table or sequence generator definition with an actual generated property, use the same name in both the definition name and the generator value generator as shown below.
@Id
@GeneratedValue(
strategy=GenerationType.SEQUENCE,
generator="SEQ_GEN")
@javax.persistence.SequenceGenerator(
name="SEQ_GEN",
sequenceName="my_sequence",
allocationSize=20
)
public Integer getId() { ... }
The scope of a generator definition can be the application or the class. Class-defined generators are not visible outside the class and can override application level generators. Application level generators are defined in JPA's XML deployment descriptors (see XXXXXX ???):
<table-generator name="EMP_GEN"
table="GENERATOR_TABLE"
pk-column-name="key"
value-column-name="hi"
pk-column-value="EMP"
allocation-size="20"/>
//and the annotation equivalent
@javax.persistence.TableGenerator(
name="EMP_GEN",
table="GENERATOR_TABLE",
pkColumnName = "key",
valueColumnName = "hi"
pkColumnValue="EMP",
allocationSize=20
)
<sequence-generator name="SEQ_GEN"
sequence-name="my_sequence"
allocation-size="20"/>
//and the annotation equivalent
@javax.persistence.SequenceGenerator(
name="SEQ_GEN",
sequenceName="my_sequence",
allocationSize=20
)
If a JPA XML descriptor (like META-INF/orm.xml) is used to define the generators, EMP_GEN and SEQ_GEN are application level generators.
Package level definition is not supported by the JPA specification. However, you can use the @GenericGenerator at the package level (see ???).
These are the four standard JPA generators. Hibernate goes beyond that and provide additional generators or additional options as we will see below. You can also write your own custom identifier generator by implementing org.hibernate.id.IdentifierGenerator.
To define a custom generator, use the @GenericGenerator annotation (and its plural counter part @GenericGenerators) that describes the class of the identifier generator or its short cut name (as described below) and a list of key/value parameters. When using @GenericGenerator and assigning it via @GeneratedValue.generator, the @GeneratedValue.strategy is ignored: leave it blank.
@Id @GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid", strategy = "uuid")
public String getId() {
@Id @GeneratedValue(generator="trigger-generated")
@GenericGenerator(
name="trigger-generated",
strategy = "select",
parameters = @Parameter(name="key", value = "socialSecurityNumber")
)
public String getId() {
The hbm.xml approach uses the optional <generator> child element inside <id>. If any parameters are required to configure or initialize the generator instance, they are passed using the <param> element.
<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 アルゴリズムを使います。指定されたデータベースシーケンスを与えます。
uuidGenerates a 128-bit UUID based on a custom algorithm. The value generated is represented as a string of 32 hexidecimal digits. Users can also configure it to use a separator (config parameter "separator") which separates the hexidecimal digits into 8{sep}8{sep}4{sep}8{sep}4. Note specifically that this is different than the IETF RFC 4122 representation of 8-4-4-4-12. If you need RFC 4122 compliant UUIDs, consider using "uuid2" generator discussed below.
uuid2Generates a IETF RFC 4122 compliant (variant 2) 128-bit UUID. The exact "version" (the RFC term) generated depends on the pluggable "generation strategy" used (see below). Capable of generating values as java.util.UUID, java.lang.String or as a byte array of length 16 (byte[16]). The "generation strategy" is defined by the interface org.hibernate.id.UUIDGenerationStrategy. The generator defines 2 configuration parameters for defining which generation strategy to use:
uuid_gen_strategy_classNames the UUIDGenerationStrategy class to use
uuid_gen_strategyNames the UUIDGenerationStrategy instance to use
Out of the box, comes with the following strategies:
org.hibernate.id.uuid.StandardRandomStrategy (the default) - generates "version 3" (aka, "random") UUID values via the randomUUID method of java.util.UUID
org.hibernate.id.uuid.CustomVersionOneStrategy - generates "version 1" UUID values, using IP address since mac address not available. If you need mac address to be used, consider leveraging one of the existing third party UUID generators which sniff out mac address and integrating it via the org.hibernate.id.UUIDGenerationStrategy contract. Two such libraries known at time of this writing include http://johannburkard.de/software/uuid/ and http://commons.apache.org/sandbox/id/uuid.html
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つを選択しますが、これは使用しているデータベースの能力に依存します。
If you want the application to assign identifiers, as opposed to having Hibernate generate them, you can use the assigned generator. This special generator uses the identifier value already assigned to the object's identifier property. The generator is used when the primary key is a natural key instead of a surrogate key. This is the default behavior if you do not specify @GeneratedValue nor <generator> elements.
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 という名前の代理キーがあります。
Finally, you can ask Hibernate to copy the identifier from another associated entity. In the Hibernate jargon, it is known as a foreign generator but the JPA mapping reads better and is encouraged.
@Entity
class MedicalHistory implements Serializable {
@Id @OneToOne
@JoinColumn(name = "person_id")
Person patient;
}
@Entity
public class Person implements Serializable {
@Id @GeneratedValue Integer id;
}
Or alternatively
@Entity
class MedicalHistory implements Serializable {
@Id Integer id;
@MapsId @OneToOne
@JoinColumn(name = "patient_id")
Person patient;
}
@Entity
class Person {
@Id @GeneratedValue Integer id;
}
In hbm.xml use the following approach:
<class name="MedicalHistory">
<id name="id">
<generator class="foreign">
<param name="property">patient</param>
</generator>
</id>
<one-to-one name="patient" class="Person" constrained="true"/>
</class>
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.
Hibernate supports the automatic generation of some of the identifier properties. Simply use the @GeneratedValue annotation on one or several id properties.
The Hibernate team has always felt such a construct as fundamentally wrong. Try hard to fix your data model before using this feature.
@Entity
public class CustomerInventory implements Serializable {
@Id
@TableGenerator(name = "inventory",
table = "U_SEQUENCES",
pkColumnName = "S_ID",
valueColumnName = "S_NEXTNUM",
pkColumnValue = "inventory",
allocationSize = 1000)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "inventory")
Integer id;
@Id @ManyToOne(cascade = CascadeType.MERGE)
Customer customer;
}
@Entity
public class Customer implements Serializable {
@Id
private int id;
}
You can also generate properties inside an @EmbeddedId class.
When using long transactions or conversations that span several database transactions, it is useful to store versioning data to ensure that if the same entity is updated by two conversations, the last to commit changes will be informed and not override the other conversation's work. It guarantees some isolation while still allowing for good scalability and works particularly well in read-often write-sometimes situations.
You can use two approaches: a dedicated version number or a timestamp.
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.
You can add optimistic locking capability to an entity using the @Version annotation:
@Entity
public class Flight implements Serializable {
...
@Version
@Column(name="OPTLOCK")
public Integer getVersion() { ... }
}
The version property will be mapped to the OPTLOCK column, and the entity manager will use it to detect conflicting updates (preventing lost updates you might otherwise see with the last-commit-wins strategy).
The version column may be a numeric. Hibernate supports any kind of type provided that you define and implement the appropriate UserVersionType.
The application must not alter the version number set up by Hibernate in any way. To artificially increase the version number, check in Hibernate Entity Manager's reference documentation LockModeType.OPTIMISTIC_FORCE_INCREMENT or LockModeType.PESSIMISTIC_FORCE_INCREMENT.
If the version number is generated by the database (via a trigger for example), make sure to use @org.hibernate.annotations.Generated(GenerationTime.ALWAYS).
To declare a version property in hbm.xml, use:
<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|."
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Alternatively, you can use a timestamp. Timestamps are a less safe implementation of optimistic locking. However, sometimes an application might use the timestamps in other ways as well.
Simply mark a property of type Date or Calendar as @Version.
@Entity
public class Flight implements Serializable {
...
@Version
public Date getLastUpdate() { ... }
}
When using timestamp versioning you can tell Hibernate where to retrieve the timestamp value from - database or JVM - by optionally adding the @org.hibernate.annotations.Source annotation to the property. Possible values for the value attribute of the annotation are org.hibernate.annotations.SourceType.VM and org.hibernate.annotations.SourceType.DB. The default is SourceType.DB which is also used in case there is no @Source annotation at all.
Like in the case of version numbers, the timestamp can also be generated by the database instead of Hibernate. To do that, use @org.hibernate.annotations.Generated(GenerationTime.ALWAYS).
In hbm.xml, use the <timestamp> element:
<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"> と等価であることに注意してください。
You need to decide which property needs to be made persistent in a given entity. This differs slightly between the annotation driven metadata and the hbm.xml files.
In the annotations world, every non static non transient property (field or method depending on the access type) of an entity is considered persistent, unless you annotate it as @Transient. Not having an annotation for your property is equivalent to the appropriate @Basic annotation.
The @Basic annotation allows you to declare the fetching strategy for a property. If set to LAZY, specifies that this property should be fetched lazily when the instance variable is first accessed. It requires build-time bytecode instrumentation, if your classes are not instrumented, property level lazy loading is silently ignored. The default is EAGER. You can also mark a property as not optional thanks to the @Basic.optional attribute. This will ensure that the underlying column are not nullable (if possible). Note that a better approach is to use the @NotNull annotation of the Bean Validation specification.
Let's look at a few examples:
public transient int counter; //transient property
private String firstname; //persistent property
@Transient
String getLengthInMeter() { ... } //transient property
String getName() {... } // persistent property
@Basic
int getLength() { ... } // persistent property
@Basic(fetch = FetchType.LAZY)
String getDetailedComment() { ... } // persistent property
@Temporal(TemporalType.TIME)
java.util.Date getDepartureTime() { ... } // persistent property
@Enumerated(EnumType.STRING)
Starred getNote() { ... } //enum persisted as String in database
counter, a transient field, and lengthInMeter, a method annotated as @Transient, and will be ignored by the Hibernate. name, length, and firstname properties are mapped persistent and eagerly fetched (the default for simple properties). The detailedComment property value will be lazily fetched from the database once a lazy property of the entity is accessed for the first time. Usually you don't need to lazy simple properties (not to be confused with lazy association fetching). The recommended alternative is to use the projection capability of JP-QL (Java Persistence Query Language) or Criteria queries.
JPA support property mapping of all basic types supported by Hibernate (all basic Java types , their respective wrappers and serializable classes). Hibernate Annotations supports out of the box enum type mapping either into a ordinal column (saving the enum ordinal) or a string based column (saving the enum string representation): the persistence representation, defaulted to ordinal, can be overridden through the @Enumerated annotation as shown in the note property example.
In plain Java APIs, the temporal precision of time is not defined. When dealing with temporal data you might want to describe the expected precision in database. Temporal data can have DATE, TIME, or TIMESTAMP precision (ie the actual date, only the time, or both). Use the @Temporal annotation to fine tune that.
@Lob indicates that the property should be persisted in a Blob or a Clob depending on the property type: java.sql.Clob, Character[], char[] and java.lang.String will be persisted in a Clob. java.sql.Blob, Byte[], byte[] and Serializable type will be persisted in a Blob.
@Lob
public String getFullText() {
return fullText;
}
@Lob
public byte[] getFullCode() {
return fullCode;
}
If the property type implements java.io.Serializable and is not a basic type, and if the property is not annotated with @Lob, then the Hibernate serializable type is used.
You can also manually specify a type using the @org.hibernate.annotations.Type and some parameters if needed. @Type.type could be:
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 )。
If you do not specify a type, Hibernate will use reflection upon the named property and guess the correct Hibernate type. Hibernate will attempt to interpret the name of the return class of the property getter using, in order, rules 2, 3, and 4.
@org.hibernate.annotations.TypeDef and @org.hibernate.annotations.TypeDefs allows you to declare type definitions. These annotations can be placed at the class or package level. Note that these definitions are global for the session factory (even when defined at the class level). If the type is used on a single entity, you can place the definition on the entity itself. Otherwise, it is recommended to place the definition at the package level. In the example below, when Hibernate encounters a property of class PhoneNumer, it delegates the persistence strategy to the custom mapping type PhoneNumberType. However, properties belonging to other classes, too, can delegate their persistence strategy to PhoneNumberType, by explicitly using the @Type annotation.
Package level annotations are placed in a file named package-info.java in the appropriate package. Place your annotations before the package declaration.
@TypeDef(
name = "phoneNumber",
defaultForType = PhoneNumber.class,
typeClass = PhoneNumberType.class
)
@Entity
public class ContactDetails {
[...]
private PhoneNumber localPhoneNumber;
@Type(type="phoneNumber")
private OverseasPhoneNumber overseasPhoneNumber;
[...]
}
The following example shows the usage of the parameters attribute to customize the TypeDef.
//in org/hibernate/test/annotations/entity/package-info.java
@TypeDefs(
{
@TypeDef(
name="caster",
typeClass = CasterStringType.class,
parameters = {
@Parameter(name="cast", value="lower")
}
)
}
)
package org.hibernate.test.annotations.entity;
//in org/hibernate/test/annotations/entity/Forest.java
public class Forest {
@Type(type="caster")
public String getSmallText() {
...
}
When using composite user type, you will have to express column definitions. The @Columns has been introduced for that purpose.
@Type(type="org.hibernate.test.annotations.entity.MonetaryAmountUserType")
@Columns(columns = {
@Column(name="r_amount"),
@Column(name="r_currency")
})
public MonetaryAmount getAmount() {
return amount;
}
public class MonetaryAmount implements Serializable {
private BigDecimal amount;
private Currency currency;
...
}
By default the access type of a class hierarchy is defined by the position of the @Id or @EmbeddedId annotations. If these annotations are on a field, then only fields are considered for persistence and the state is accessed via the field. If there annotations are on a getter, then only the getters are considered for persistence and the state is accessed via the getter/setter. That works well in practice and is the recommended approach.
The placement of annotations within a class hierarchy has to be consistent (either field or on property) to be able to determine the default access type. It is recommended to stick to one single annotation placement strategy throughout your whole application.
However in some situations, you need to:
force the access type of the entity hierarchy
override the access type of a specific entity in the class hierarchy
override the access type of an embeddable type
The best use case is an embeddable class used by several entities that might not use the same access type. In this case it is better to force the access type at the embeddable class level.
To force the access type on a given class, use the @Access annotation as showed below:
@Entity
public class Order {
@Id private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Embedded private Address address;
public Address getAddress() { return address; }
public void setAddress() { this.address = address; }
}
@Entity
public class User {
private Long id;
@Id public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
private Address address;
@Embedded public Address getAddress() { return address; }
public void setAddress() { this.address = address; }
}
@Embeddable
@Access(AcessType.PROPERTY)
public class Address {
private String street1;
public String getStreet1() { return street1; }
public void setStreet1() { this.street1 = street1; }
private hashCode; //not persistent
}
You can also override the access type of a single property while keeping the other properties standard.
@Entity
public class Order {
@Id private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Transient private String userId;
@Transient private String orderId;
@Access(AccessType.PROPERTY)
public String getOrderNumber() { return userId + ":" + orderId; }
public void setOrderNumber() { this.userId = ...; this.orderId = ...; }
}
In this example, the default access type is FIELD except for the orderNumber property. Note that the corresponding field, if any must be marked as @Transient or transient.
The annotation @org.hibernate.annotations.AccessType should be considered deprecated for FIELD and PROPERTY access. It is still useful however if you need to use a custom access type.
It is sometimes useful to avoid increasing the version number even if a given property is dirty (particularly collections). You can do that by annotating the property (or collection) with @OptimisticLock(excluded=true).
More formally, specifies that updates to this property do not require acquisition of the optimistic lock.
The column(s) used for a property mapping can be defined using the @Column annotation. Use it to override default values (see the JPA specification for more information on the defaults). You can use this annotation at the property level for properties that are:
not annotated at all
annotated with @Basic
annotated with @Version
annotated with @Lob
annotated with @Temporal
@Entity
public class Flight implements Serializable {
...
@Column(updatable = false, name = "flight_name", nullable = false, length=50)
public String getName() { ... }
The name property is mapped to the flight_name column, which is not nullable, has a length of 50 and is not updatable (making the property immutable).
This annotation can be applied to regular properties as well as @Id or @Version properties.
@Column(
name="colu
mnName";
boolean un
ique() default false;
boolean nu
llable() default true;
boolean in
sertable() default true;
boolean up
datable() default true;
String col
umnDefinition() default "";
String tab
le() default "";
int length
() default 255;
int precis
ion() default 0; // decimal precision
int scale(
) default 0; // decimal scale
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sometimes, you want the Database to do some computation for you rather than in the JVM, you might also create some kind of virtual column. You can use a SQL fragment (aka formula) instead of mapping a property into a column. This kind of property is read only (its value is calculated by your formula fragment).
@Formula("obj_length * obj_height * obj_width")
public long getObjectVolume()
The SQL fragment can be as complex as you want and even include subselects.
If a property is not annotated, the following rules apply:
If the property is of a single type, it is mapped as @Basic
Otherwise, if the type of the property is annotated as @Embeddable, it is mapped as @Embedded
Otherwise, if the type of the property is Serializable, it is mapped as @Basic in a column holding the object in its serialized version
Otherwise, if the type of the property is java.sql.Clob or java.sql.Blob, it is mapped as @Lob with the appropriate LobType
<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> マッピング要素を使えることにも注意してください。
Embeddable objects (or components) are objects whose properties are mapped to the same table as the owning entity's table. Components can, in turn, declare their own properties, components or collections
It is possible to declare an embedded component inside an entity and even override its column mapping. Component classes have to be annotated at the class level with the @Embeddable annotation. It is possible to override the column mapping of an embedded object for a particular entity using the @Embedded and @AttributeOverride annotation in the associated property:
@Entity
public class Person implements Serializable {
// Persistent component using defaults
Address homeAddress;
@Embedded
@AttributeOverrides( {
@AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),
@AttributeOverride(name="name", column = @Column(name="bornCountryName") )
} )
Country bornIn;
...
}
@Embeddable
public class Address implements Serializable {
String city;
Country nationality; //no overriding here
}
@Embeddable
public class Country implements Serializable {
private String iso2;
@Column(name="countryName") private String name;
public String getIso2() { return iso2; }
public void setIso2(String iso2) { this.iso2 = iso2; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
...
}
An embeddable object inherits the access type of its owning entity (note that you can override that using the @Access annotation).
The Person entity has two component properties, homeAddress and bornIn. homeAddress property has not been annotated, but Hibernate will guess that it is a persistent component by looking for the @Embeddable annotation in the Address class. We also override the mapping of a column name (to bornCountryName) with the @Embedded and @AttributeOverride annotations for each mapped attribute of Country. As you can see, Country is also a nested component of Address, again using auto-detection by Hibernate and JPA defaults. Overriding columns of embedded objects of embedded objects is through dotted expressions.
@Embedded
@AttributeOverrides( {
@AttributeOverride(name="city", column = @Column(name="fld_city") ),
@AttributeOverride(name="nationality.iso2", column = @Column(name="nat_Iso2") ),
@AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName") )
//nationality columns in homeAddress are overridden
} )
Address homeAddress;
Hibernate Annotations supports something that is not explicitly supported by the JPA specification. You can annotate a embedded object with the @MappedSuperclass annotation to make the superclass properties persistent (see @MappedSuperclass for more informations).
You can also use association annotations in an embeddable object (ie @OneToOne, @ManyToOne, @OneToMany or @ManyToMany). To override the association columns you can use @AssociationOverride.
If you want to have the same embeddable object type twice in the same entity, the column name defaulting will not work as several embedded objects would share the same set of columns. In plain JPA, you need to override at least one set of columns. Hibernate, however, allows you to enhance the default naming mechanism through the NamingStrategy interface. You can write a strategy that prevent name clashing in such a situation. DefaultComponentSafeNamingStrategy is an example of this.
If a property of the embedded object points back to the owning entity, annotate it with the @Parent annotation. Hibernate will make sure this property is properly loaded with the entity reference.
In XML, use the <component> element.
<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. This feature is not supported in annotations.
Java is a language supporting polymorphism: a class can inherit from another. Several strategies are possible to persist a class hierarchy:
Single table per class hierarchy strategy: a single table hosts all the instances of a class hierarchy
Joined subclass strategy: one table per class and subclass is present and each table persist the properties specific to a given subclass. The state of the entity is then stored in its corresponding class table and all its superclasses
Table per class strategy: one table per concrete class and subclass is present and each table persist the properties of the class and its superclasses. The state of the entity is then stored entirely in the dedicated table for its class.
With this approach the properties of all the subclasses in a given mapped class hierarchy are stored in a single table.
Each subclass declares its own persistent properties and subclasses. Version and id properties are assumed to be inherited from the root class. Each subclass in a hierarchy must define a unique discriminator value. If this is not specified, the fully qualified Java class name is used.
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
name="planetype",
discriminatorType=DiscriminatorType.STRING
)
@DiscriminatorValue("Plane")
public class Plane { ... }
@Entity
@DiscriminatorValue("A320")
public class A320 extends Plane { ... }
In hbm.xml, for the table-per-class-hierarchy mapping strategy, the <subclass> declaration is used. For example:
<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>
|
|
|
|
|
|
|
|
For information about inheritance mappings see 10章継承マッピング.
Discriminators are required for polymorphic persistence using the table-per-class-hierarchy mapping strategy. It declares a discriminator column of the table. The discriminator column contains marker values that tell the persistence layer what subclass to instantiate for a particular row. Hibernate Core supports the follwoing restricted set of types as discriminator column: string, character, integer, byte, short, boolean, yes_no, true_false.
Use the @DiscriminatorColumn to define the discriminator column as well as the discriminator type.
The enum DiscriminatorType used in javax.persitence.DiscriminatorColumn only contains the values STRING, CHAR and INTEGER which means that not all Hibernate supported types are available via the @DiscriminatorColumn annotation.
You can also use @DiscriminatorFormula to express in SQL a virtual discriminator column. This is particularly useful when the discriminator value can be extracted from one or more columns of the table. Both @DiscriminatorColumn and @DiscriminatorFormula are to be set on the root entity (once per persisted hierarchy).
@org.hibernate.annotations.DiscriminatorOptions allows to optionally specify Hibernate specific discriminator options which are not standardized in JPA. The available options are force and insert. The force attribute is useful if the table contains rows with "extra" discriminator values that are not mapped to a persistent class. This could for example occur when working with a legacy database. If force is set to true Hibernate will specify the allowed discriminator values in the SELECT query, even when retrieving all instances of the root class. The second option - insert - tells Hibernate whether or not to include the discriminator column in SQL INSERTs. Usually the column should be part of the INSERT statement, but if your discriminator column is also part of a mapped composite identifier you have to set this option to false.
There is also a @org.hibernate.annotations.ForceDiscriminator annotation which is deprecated since version 3.6. Use @DiscriminatorOptions instead.
Finally, use @DiscriminatorValue on each class of the hierarchy to specify the value stored in the discriminator column for a given entity. If you do not set @DiscriminatorValue on a class, the fully qualified class name is used.
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
name="planetype",
discriminatorType=DiscriminatorType.STRING
)
@DiscriminatorValue("Plane")
public class Plane { ... }
@Entity
@DiscriminatorValue("A320")
public class A320 extends Plane { ... }
In hbm.xml, the <discriminator> element is used to define the discriminator column or formula:
<discriminator
column
="discriminator_column"
type="
discriminator_type"
force=
"true|false"
insert
="true|false"
formul
a="arbitrary sql expression"
/>
|
|
|
|
|
|
|
|
|
|
識別カラムの実際の値は、 <class> と <subclass> 要素の discriminator-value 属性で指定されます。
formula 属性を使うと、行の型を評価するために任意の SQL 式を宣言できます:
<discriminator
formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
type="integer"/>
Each subclass can also be mapped to its own table. This is called the table-per-subclass mapping strategy. An inherited state is retrieved by joining with the table of the superclass. A discriminator column is not required for this mapping strategy. Each subclass must, however, declare a table column holding the object identifier. The primary key of this table is also a foreign key to the superclass table and described by the @PrimaryKeyJoinColumns or the <key> element.
@Entity @Table(name="CATS")
@Inheritance(strategy=InheritanceType.JOINED)
public class Cat implements Serializable {
@Id @GeneratedValue(generator="cat-uuid")
@GenericGenerator(name="cat-uuid", strategy="uuid")
String getId() { return id; }
...
}
@Entity @Table(name="DOMESTIC_CATS")
@PrimaryKeyJoinColumn(name="CAT")
public class DomesticCat extends Cat {
public String getName() { return name; }
}
The table name still defaults to the non qualified class name. Also if @PrimaryKeyJoinColumn is not set, the primary key / foreign key columns are assumed to have the same names as the primary key columns of the primary table of the superclass.
In hbm.xml, use the <joined-subclass> element. For example:
<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>
|
|
|
|
|
|
|
|
Use the <key> element to declare the primary key / foreign key column. The mapping at the start of the chapter would then be re-written as:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/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 10章継承マッピング.
A third option is to map only the concrete classes of an inheritance hierarchy to tables. This is called the table-per-concrete-class strategy. Each table defines all persistent states of the class, including the inherited state. In Hibernate, it is not necessary to explicitly map such inheritance hierarchies. You can map each class as a separate entity root. However, if you wish use polymorphic associations (e.g. an association to the superclass of your hierarchy), you need to use the union subclass mapping.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Flight implements Serializable { ... }
Or in hbm.xml:
<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 10章継承マッピング.
This is sometimes useful to share common properties through a technical or a business superclass without including it as a regular mapped entity (ie no specific table for this entity). For that purpose you can map them as @MappedSuperclass.
@MappedSuperclass
public class BaseEntity {
@Basic
@Temporal(TemporalType.TIMESTAMP)
public Date getLastUpdate() { ... }
public String getLastUpdater() { ... }
...
}
@Entity class Order extends BaseEntity {
@Id public Integer getId() { ... }
...
}
In database, this hierarchy will be represented as an Order table having the id, lastUpdate and lastUpdater columns. The embedded superclass property mappings are copied into their entity subclasses. Remember that the embeddable superclass is not the root of the hierarchy though.
Properties from superclasses not mapped as @MappedSuperclass are ignored.
The default access type (field or methods) is used, unless you use the @Access annotation.
The same notion can be applied to @Embeddable objects to persist properties from their superclasses. You also need to use @MappedSuperclass to do that (this should not be considered as a standard EJB3 feature though)
It is allowed to mark a class as @MappedSuperclass in the middle of the mapped inheritance hierarchy.
Any class in the hierarchy non annotated with @MappedSuperclass nor @Entity will be ignored.
You can override columns defined in entity superclasses at the root entity level using the @AttributeOverride annotation.
@MappedSuperclass
public class FlyingObject implements Serializable {
public int getAltitude() {
return altitude;
}
@Transient
public int getMetricAltitude() {
return metricAltitude;
}
@ManyToOne
public PropulsionType getPropulsion() {
return metricAltitude;
}
...
}
@Entity
@AttributeOverride( name="altitude", column = @Column(name="fld_altitude") )
@AssociationOverride(
name="propulsion",
joinColumns = @JoinColumn(name="fld_propulsion_fk")
)
public class Plane extends FlyingObject {
...
}
The altitude property will be persisted in an fld_altitude column of table Plane and the propulsion association will be materialized in a fld_propulsion_fk foreign key column.
You can define @AttributeOverride(s) and @AssociationOverride(s) on @Entity classes, @MappedSuperclass classes and properties pointing to an @Embeddable object.
In hbm.xml, simply map the properties of the superclass in the <class> element of the entity that needs to inherit them.
While not recommended for a fresh schema, some legacy databases force your to map a single entity on several tables.
Using the @SecondaryTable or @SecondaryTables class level annotations. To express that a column is in a particular table, use the table parameter of @Column or @JoinColumn.
@Entity
@Table(name="MainCat")
@SecondaryTables({
@SecondaryTable(name="Cat1", pkJoinColumns={
@PrimaryKeyJoinColumn(name="cat_id", referencedColumnName="id")
),
@SecondaryTable(name="Cat2", uniqueConstraints={@UniqueConstraint(columnNames={"storyPart2"})})
})
public class Cat implements Serializable {
private Integer id;
private String name;
private String storyPart1;
private String storyPart2;
@Id @GeneratedValue
public Integer getId() {
return id;
}
public String getName() {
return name;
}
@Column(table="Cat1")
public String getStoryPart1() {
return storyPart1;
}
@Column(table="Cat2")
public String getStoryPart2() {
return storyPart2;
}
}
In this example, name will be in MainCat. storyPart1 will be in Cat1 and storyPart2 will be in Cat2. Cat1 will be joined to MainCat using the cat_id as a foreign key, and Cat2 using id (ie the same column name, the MainCat id column has). Plus a unique constraint on storyPart2 has been set.
There is also additional tuning accessible via the @org.hibernate.annotations.Table annotation:
fetch: If set to JOIN, the default, Hibernate will use an inner join to retrieve a secondary table defined by a class or its superclasses and an outer join for a secondary table defined by a subclass. If set to SELECT then Hibernate will use a sequential select for a secondary table defined on a subclass, which will be issued only if a row turns out to represent an instance of the subclass. Inner joins will still be used to retrieve a secondary defined by the class and its superclasses.
inverse: If true, Hibernate will not try to insert or update the properties defined by this join. Default to false.
optional: If enabled (the default), Hibernate will insert a row only if the properties defined by this join are non-null and will always use an outer join to retrieve the properties.
foreignKey: defines the Foreign Key name of a secondary table pointing back to the primary table.
Make sure to use the secondary table name in the appliesto property
@Entity
@Table(name="MainCat")
@SecondaryTable(name="Cat1")
@org.hibernate.annotations.Table(
appliesTo="Cat1",
fetch=FetchMode.SELECT,
optional=true)
public class Cat implements Serializable {
private Integer id;
private String name;
private String storyPart1;
private String storyPart2;
@Id @GeneratedValue
public Integer getId() {
return id;
}
public String getName() {
return name;
}
@Column(table="Cat1")
public String getStoryPart1() {
return storyPart1;
}
@Column(table="Cat2")
public String getStoryPart2() {
return storyPart2;
}
}
In hbm.xml, use the <join> element.
<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つのクラス階層で継承のマッピング戦略を切り替える時には有用です。
To link one entity to an other, you need to map the association property as a to one association. In the relational model, you can either use a foreign key or an association table, or (a bit less common) share the same primary key value between the two entities.
To mark an association, use either @ManyToOne or @OnetoOne.
@ManyToOne and @OneToOne have a parameter named targetEntity which describes the target entity name. You usually don't need this parameter since the default value (the type of the property that stores the association) is good in almost all cases. However this is useful when you want to use interfaces as the return type instead of the regular entity.
Setting a value of the cascade attribute to any meaningful value other than nothing will propagate certain operations to the associated object. The meaningful values are divided into three categories.
basic operations, which include: persist, merge, delete, save-update, evict, replicate, lock and refresh;
special values: delete-orphan or 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 associations do not support orphan delete.
By default, single point associations are eagerly fetched in JPA 2. You can mark it as lazily fetched by using @ManyToOne(fetch=FetchType.LAZY) in which case Hibernate will proxy the association and load it when the state of the associated entity is reached. You can force Hibernate not to use a proxy by using @LazyToOne(NO_PROXY). In this case, the property is fetched lazily when the instance variable is first accessed. This requires build-time bytecode instrumentation. lazy="false" specifies that the association will always be eagerly fetched.
With the default JPA options, single-ended associations are loaded with a subsequent select if set to LAZY, or a SQL JOIN is used for EAGER associations. You can however adjust the fetching strategy, ie how data is fetched by using @Fetch. FetchMode can be SELECT (a select is triggered when the association needs to be loaded) or JOIN (use a SQL JOIN to load the association while loading the owner entity). JOIN overrides any lazy attribute (an association loaded through a JOIN strategy cannot be lazy).
An ordinary association to another persistent class is declared using a
@ManyToOne if several entities can point to the the target entity
@OneToOne if only a single entity can point to the the target entity
and a foreign key in one table is referencing the primary key column(s) of the target table.
@Entity
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
@JoinColumn(name="COMP_ID")
public Company getCompany() {
return company;
}
...
}
The @JoinColumn attribute is optional, the default value(s) is the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column in the owned side. In this example company_id because the property name is company and the column id of Company is id.
@Entity
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=CompanyImpl.class )
@JoinColumn(name="COMP_ID")
public Company getCompany() {
return company;
}
...
}
public interface Company {
...
}
You can also map a to one association through an association table. This association table described by the @JoinTable annotation will contains a foreign key referencing back the entity table (through @JoinTable.joinColumns) and a a foreign key referencing the target entity table (through @JoinTable.inverseJoinColumns).
@Entity
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
@JoinTable(name="Flight_Company",
joinColumns = @JoinColumn(name="FLIGHT_ID"),
inverseJoinColumns = @JoinColumn(name="COMP_ID")
)
public Company getCompany() {
return company;
}
...
}
You can use a SQL fragment to simulate a physical join column using the @JoinColumnOrFormula / @JoinColumnOrformulas annotations (just like you can use a SQL fragment to simulate a property column via the @Formula annotation).
@Entity
public class Ticket implements Serializable {
@ManyToOne
@JoinColumnOrFormula(formula="(firstname + ' ' + lastname)")
public Person getOwner() {
return person;
}
...
}
You can mark an association as mandatory by using the optional=false attribute. We recommend to use Bean Validation's @NotNull annotation as a better alternative however. As a consequence, the foreign key column(s) will be marked as not nullable (if possible).
When Hibernate cannot resolve the association because the expected associated element is not in database (wrong id on the association column), an exception is raised. This might be inconvenient for legacy and badly maintained schemas. You can ask Hibernate to ignore such elements instead of raising an exception using the @NotFound annotation.
例5.1 @NotFound annotation
@Entity
public class Child {
...
@ManyToOne
@NotFound(action=NotFoundAction.IGNORE)
public Parent getParent() { ... }
...
}
Sometimes you want to delegate to your database the deletion of cascade when a given entity is deleted. In this case Hibernate generates a cascade delete constraint at the database level.
例5.2 @OnDelete annotation
@Entity
public class Child {
...
@ManyToOne
@OnDelete(action=OnDeleteAction.CASCADE)
public Parent getParent() { ... }
...
}
Foreign key constraints, while generated by Hibernate, have a fairly unreadable name. You can override the constraint name using @ForeignKey.
例5.3 @ForeignKey annotation
@Entity
public class Child {
...
@ManyToOne
@ForeignKey(name="FK_PARENT")
public Parent getParent() { ... }
...
}
alter table Child add constraint FK_PARENT foreign key (parent_id) references Parent
Sometimes, you want to link one entity to an other not by the target entity primary key but by a different unique key. You can achieve that by referencing the unique key column(s) in @JoinColumn.referenceColumnName.
@Entity
class Person {
@Id Integer personNumber;
String firstName;
@Column(name="I")
String initial;
String lastName;
}
@Entity
class Home {
@ManyToOne
@JoinColumns({
@JoinColumn(name="first_name", referencedColumnName="firstName"),
@JoinColumn(name="init", referencedColumnName="I"),
@JoinColumn(name="last_name", referencedColumnName="lastName"),
})
Person owner
}
This is not encouraged however and should be reserved to legacy mappings.
In hbm.xml, mapping an association is similar. The main difference is that a @OneToOne is mapped as <many-to-one unique="true"/>, let's dive into the subject.
<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"/>
The second approach is to ensure an entity and its associated entity share the same primary key. In this case the primary key column is also a foreign key and there is no extra column. These associations are always one to one.
例5.4 One to One association
@Entity
public class Body {
@Id
public Long getId() { return id; }
@OneToOne(cascade = CascadeType.ALL)
@MapsId
public Heart getHeart() {
return heart;
}
...
}
@Entity
public class Heart {
@Id
public Long getId() { ...}
}
Many people got confused by these primary key based one to one associations. They can only be lazily loaded if Hibernate knows that the other side of the association is always present. To indicate to Hibernate that it is the case, use @OneToOne(optional=false).
In hbm.xml, use the following mapping.
<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つのオブジェクトを主キー関連によって関連付けたいのであれば、確実に同じ識別子の値を代入しなければなりません。
主キー関連を行うためには、以下のマッピングを 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 インスタンスとして同じ主キーが割り当てられます。
Although we recommend the use of surrogate keys as primary keys, you should try to identify natural keys for all entities. A natural key is a property or combination of properties that is unique and non-null. It is also immutable. Map the properties of the natural key as @NaturalId or map them inside the <natural-id> element. Hibernate will generate the necessary unique key and nullability constraints and, as a result, your mapping will be more self-documenting.
@Entity
public class Citizen {
@Id
@GeneratedValue
private Integer id;
private String firstname;
private String lastname;
@NaturalId
@ManyToOne
private State state;
@NaturalId
private String ssn;
...
}
//and later on query
List results = s.createCriteria( Citizen.class )
.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", ste ) )
.list();
Or in XML,
<natural-id mutable="true|false"/>
<property ... />
<many-to-one ... />
......
</natural-id>
エンティティの自然キープロパティの比較には、 equals() と hashCode() の実装を強くお勧めします。
このマッピングは自然主キーを使ったエンティティでの使用を意図していません。
mutable (オプション、 デフォルトは false ): デフォルトでは、自然識別子プロパティは不変(定数)と想定されています。
There is one more type of property mapping. The @Any mapping defines a polymorphic association to classes from multiple tables. This type of mapping requires more than one column. The first column contains the type of the associated entity. The remaining columns contain the identifier. It is impossible to specify a foreign key constraint for this kind of association. This is not the usual way of mapping polymorphic associations and you should use this only in special cases. For example, for audit logs, user session data, etc.
The @Any annotation describes the column holding the metadata information. To link the value of the metadata information and an actual entity type, The @AnyDef and @AnyDefs annotations are used. The metaType attribute allows the application to specify a custom type that maps database column values to persistent classes that have identifier properties of the type specified by idType. You must specify the mapping from values of the metaType to class names.
@Any( metaColumn = @Column( name = "property_type" ), fetch=FetchType.EAGER )
@AnyMetaDef(
idType = "integer",
metaType = "string",
metaValues = {
@MetaValue( value = "S", targetEntity = StringProperty.class ),
@MetaValue( value = "I", targetEntity = IntegerProperty.class )
} )
@JoinColumn( name = "property_id" )
public Property getMainProperty() {
return mainProperty;
}
Note that @AnyDef can be mutualized and reused. It is recommended to place it as a package metadata in this case.
//on a package
@AnyMetaDef( name="property"
idType = "integer",
metaType = "string",
metaValues = {
@MetaValue( value = "S", targetEntity = StringProperty.class ),
@MetaValue( value = "I", targetEntity = IntegerProperty.class )
} )
package org.hibernate.test.annotations.any;
//in a class
@Any( metaDef="property", metaColumn = @Column( name = "property_type" ), fetch=FetchType.EAGER )
@JoinColumn( name = "property_id" )
public Property getMainProperty() {
return mainProperty;
}
The hbm.xml equivalent is:
<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>
You cannot mutualize the metadata in hbm.xml as you can in annotations.
<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>
|
|
|
|
|
|
|
|
|
|
|
|
<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="owner"
class="Person" property-ref="name">
<column name="firstName"/>
<column name="initial"/>
<column name="lastName"/>
</many-to-one>
When using annotations as a mapping strategy, such construct is not necessary as the binding between a column and its related column on the associated table is done directly
@Entity
class Person {
@Id Integer personNumber;
String firstName;
@Column(name="I")
String initial;
String lastName;
}
@Entity
class Home {
@ManyToOne
@JoinColumns({
@JoinColumn(name="first_name", referencedColumnName="firstName"),
@JoinColumn(name="init", referencedColumnName="I"),
@JoinColumn(name="last_name", referencedColumnName="lastName"),
})
Person owner
}
しかし、このようなレガシーデータマッピングのコンテキスト外への使用は推奨しません。
The hbm.xml structure has some specificities naturally not present when using annotations, let's describe them briefly.
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 名前空間を認識します。
a hibernate namespace is recognized whenever the resolver encounters a systemId starting with http://www.hibernate.org/dtd/. The resolver attempts to resolve these entities via the classloader which loaded the Hibernate classes.
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 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 。
The <key> element is featured a few times within this guide. It appears anywhere the parent mapping element defines a join to a new table that references the primary key of the original table. It also defines the foreign key in the joined table:
<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"> を使ってキーカラムを宣言 しなくてはなりません 。
アプリケーションに同じ名前の2つの永続クラスがあり、 Hibernate クエリで完全修飾された(パッケージの)名前を指定したくないと仮定します。そのような場合は auto-import="true" に頼らず、クラスが「インポート」されたものであると明示できます。明示的にマッピングされていないクラスやインターフェースでさえもインポートできます。
<import class="java.lang.Object" rename="Universe"/>
<import
class=
"ClassName"
rename
="ShortName"
/>
|
|
|
|
This feature is unique to hbm.xml and is not supported in annotations.
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>
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 のバイナリ型にマッピングされます。
textMaps long Java strings to a SQL LONGVARCHAR or TEXT type.
imageMaps long byte arrays to a SQL LONGVARBINARY.
serializableシリアライズ可能な Java 型は、適切な SQL のバイナリ型にマッピングされます。デフォルトで基本型ではないシリアライズ可能な Java クラスやインターフェースの名前を指定することで、 Hibernate の型を serializable とすることもできます。
clob, blobJDBC クラス java.sql.Clob と java.sql.Blob に対する型マッピング。 blob や clob オブジェクトはトランザクションの外では再利用できないため、アプリケーションによっては不便かもしれません。(さらにはドライバサポートが一貫していません。)
materialized_clobMaps long Java strings to a SQL CLOB type. When read, the CLOB value is immediately materialized into a Java string. Some drivers require the CLOB value to be read within a transaction. Once materialized, the Java string is available outside of the transaction.
materialized_blobMaps long Java byte arrays to a SQL BLOB type. When read, the BLOB value is immediately materialized into a byte array. Some drivers require the BLOB value to be read within a transaction. Once materialized, the byte array is available outside of the transaction.
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 を使って、どのように指定されるのかに注目してください。
This feature is not supported in Annotations
マッピングドキュメントでテーブルやカラムの名前をバッククォートで囲むことで、 Hibernate で生成された SQL 中の識別子を引用させることができます。 Hibernate は SQL の Dialect に対応する、正しい引用スタイルを使います(普通はダブルクォートですが、 SQL Server ではかぎ括弧、 MySQL ではバッククォートです)。
@Entity @Table(name="`Line Item`")
class LineItem {
@id @Column(name="`Item Id`") Integer id;
@Column(name="`Item #`")&