Hibernate.org Community Documentation

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

## 第4章 永続クラス

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. 単純な POJO の例

例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.

### 4.1.1. 引数のないコンストラクタを実装する

`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 ``java.lang.reflect.Constructor`.newInstance()`. It is recommended that this constructor be defined with at least package visibility in order for runtime proxy generation to work properly.

### 4.1.2. Provide an identifier property

## 注意

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 値を取れる(つまりプリミティブではない)型を使った方がよいでしょう。

### 4.1.3. Prefer non-final classes (semi-optional)

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」.

例4.2 Disabling proxies in `hbm.xml`

    <class name="Cat" lazy="false"...>...</class>

例4.3 Disabling proxies in annotations

    @Entity @Proxy(lazy=false) public class Cat { ... }

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.4 Proxying an interface in `hbm.xml`

    <class name="Cat" proxy="ICat"...>...</class>

例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」.

### 4.1.4. 永続フィールドに対するアクセサとミューテータを定義する（オプション）

`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.

## 4.2. 継承の実装

サブクラスも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;

            }

    }

## 4.3. `equals()` と `hashCode()`の実装

以下の条件の場合、 `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.

## 4.4. 動的モデル

## 注意

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 マッピング.

## 4.5. Tuplizer

`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>

## 4.6. EntityNameResolvers

`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:

1. Implement a custom tuplizer (see 「Tuplizer」), implementing the `getEntityNameResolvers` method

2. Register it with the `org.hibernate.impl.SessionFactoryImpl` (which is the implementation class for `org.hibernate.SessionFactory`) using the `registerEntityNameResolver` method.

製作著作 © 2004 Red Hat, Inc.

- 前のページ第3章 設定
- 上に戻る
- ホーム
- 次のページ第5章 基本的な O/R マッピング