1. 概要

このチュートリアルでは、Hibernateの load()メソッドのコンテキストでプロキシが何であるかを確認します。

Hibernateを初めて使用する読者は、最初にbasicsに慣れることを検討してください。

2. プロキシとload()メソッドの簡単な紹介

定義上、プロキシは「代理として機能するか、別の代理として機能することを許可された機能」です

これは、 Session.load()を呼び出して、目的のエンティティクラスの初期化されていないプロキシと呼ばれるものを作成するときにHibernateに適用されます。

簡単に言うと、Hibernateは、 CGLib ライブラリを使用して、エンティティクラスをサブクラス化します。 @Id メソッドを除いて、プロキシ実装は他のすべてのプロパティメソッドをHibernateセッションに委任して、インスタンスにデータを入力します。

public class HibernateProxy extends MyEntity {
    private MyEntity target;

    public String getFirstName() {
        if (target == null) {
            target = readFromDatabase();
        }
        return target.getFirstName();
    }
}

このサブクラスは、データベースに直接クエリを実行する代わりに返されるサブクラスになります。

エンティティメソッドの1つが呼び出されると、エンティティが読み込まれ、その時点で初期化プロキシになります。

3. プロキシとレイジー読み込み

3.1. 単一のエンティティ

従業員をエンティティとして考えてみましょう。 まず、他のテーブルとは関係がないと仮定します。

Session.load()を使用して Employee をインスタンス化する場合:

Employee albert = session.load(Employee.class, new Long(1));

次に、Hibernateは Employee の初期化されていないプロキシを作成します。指定したIDが含まれますが、データベースにまだアクセスしていないため、他の値はありません。

ただし、albertでメソッドを呼び出すと次のようになります。

String firstName = albert.getFirstName();

次に、Hibernateは従業員データベーステーブルに主キー1のエンティティを照会し、albertに対応する行のプロパティを入力します。

行が見つからない場合、HibernateはObjectNotFoundExceptionをスローします。

3.2. 1対多の関係

それでは、 Company エンティティも作成しましょう。ここで、 Company には多くの従業員がいます:

public class Company {
    private String name;
    private Set<Employee> employees;
}

今回は会社でSession.load()を使用します。

Company bizco = session.load(Company.class, new Long(1));
String name = bizco.getName();

次に、従業員のセットが少し異なることを除いて、会社のプロパティが以前と同じように入力されます。

ほら、会社の行だけを照会しましたが、フェッチ戦略に応じて、 getEmployees を呼び出すまで、プロキシは従業員をそのままにしておきます。

3.3. 多対1の関係

ケースは反対方向に似ています:

public class Employee {
    private String firstName;
    private Company workplace;
}

load()を再度使用する場合:

Employee bob = session.load(Employee.class, new Long(2));
String firstName = bob.getFirstName();

bob が初期化され、実際には、フェッチ戦略に応じてworkplaceが初期化されていないプロキシに設定されます。

4. 怠惰な読み込み

現在、 load()は、初期化されていないプロキシを常に提供するとは限りません。 実際、 Session java doc は私たちに思い出させます(強調が追加されました):

このメソッドmightは、識別子以外のメソッドにアクセスすると、オンデマンドで初期化されるプロキシされたインスタンスを返します。

これが発生する可能性がある場合の簡単な例は、バッチサイズです。

従業員エンティティで@BatchSizeを使用しているとします。

@Entity
@BatchSize(size=5)
class Employee {
    // ...
}

今回は3人の従業員がいます。

Employee catherine = session.load(Employee.class, new Long(3));
Employee darrell = session.load(Employee.class, new Long(4));
Employee emma = session.load(Employee.class, new Long(5));

catherinegetFirstNameを呼び出すと、次のようになります。

String cathy = catherine.getFirstName();

次に、実際には、Hibernateは3人の従業員すべてを一度にロードして、3人すべてを初期化されたプロキシに変換することを決定する場合があります。

そして、 darrell の名を呼び出すと、次のようになります。

String darrell = darrell.getFirstName();

その場合、Hibernateはデータベースにまったくヒットしません。

5. 熱心な読み込み

5.1. get()の使用

プロキシを完全にバイパスし、 Session.get()を使用してHibernateに本物をロードするように依頼することもできます。

Employee finnigan = session.get(Employee.class, new Long(6));

これにより、プロキシを返す代わりに、データベースがすぐに呼び出されます。

実際には、 finnigan が存在しない場合、 ObjectNotFoundException の代わりに、nullが返されます。

5.2. パフォーマンスへの影響

get()は便利ですが、 load()はデータベースで軽量化できます。

たとえば、ジェラルドが新しい会社で働くとしましょう。

Employee gerald = session.get(Employee.class, new Long(7));
Company worldco = (Company) session.load(Company.class, new Long(2));
employee.setCompany(worldco);        
session.save(employee);

この状況では従業員レコードのみを変更することがわかっているため、会社に対して load()を呼び出すのが賢明です。

Companyget()を呼び出すと、データベースからすべてのデータが不必要に読み込まれます。

6. 結論

この記事では、 Hibernate プロキシがどのように機能し、これがloadメソッドとエンティティおよびそれらの関係にどのように影響するかを簡単に学びました。

また、 load() get()。の違いについても簡単に説明しました。

いつものように、チュートリアルに付属する完全なソースコードは、GitHubでから入手できます。