Hibernateのload()メソッド内のプロキシ

1概要

このチュートリアルでは、Hibernateの__load()メソッドとの関連でプロキシがどのようなものであるかを見ていきます。

Hibernateに慣れていない読者は、まずhttps://www.baeldung.com/hibernate-4-spring[basics]に慣れることを検討してください。

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

  • 定義により、https://www.dictionary.com/browse/proxy[proxy]は、「代理人または他の代理人として行動することを許可された機能」です。

これは、 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();
    }
}
  • このサブクラスは、データベースに直接問い合わせる代わりに返されるものです。

いずれかのエンティティメソッドが呼び出されると、エンティティがロードされ、その時点で__初期化プロキシになります。

3プロキシとLazy __load __ing

3.1. 単一のエンティティ

「従業員」を実体として考えてみましょう。はじめに、他のテーブルとは関係がないと仮定します。

Session.load() を使用して Employee をインスタンス化すると、次のようになります。

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

それからHibernateは Employee の初期化されていないプロキシを作成します。** それは我々がそれに与えたIDを含みますが、そうでなければまだデータベースにヒットしていないので他の値を持ちません。

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

String firstName = albert.getFirstName();

それからHibernateは employee データベーステーブルに主キー1を持つエンティティを問い合わせ、対応する行から彼のプロパティを持つ albert を生成します。

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

3.2. 一対多の関係

それでは、 __ Company エンティティも作成しましょう。ここで Company は多数の Employeesを持ちます。

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()は常に初期化されていないプロキシを私たちに与えるわけではありません。実際、https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/Session.html#load(java.lang.Class,%20java.io.Serializable)[ Session ____java doc]私たちに思い出させる(強調を追加):

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

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

__従業員エンティティで @ 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));

__getFirstName on catherine__を呼び出すと、

String cathy = catherine.getFirstName();
  • その後、実際には、Hibernateは3人全員を一度にロードして、3人全員を初期化済みプロキシにすることを決定する場合があります。

それから、 darrell のファーストネームを呼び出すと、

String darrell = darrell.getFirstName();

そうすれば** Hibernateはデータベースにまったくアクセスしません。

=== 5熱心なロード

==== 5.1. get() を使う

私たちはプロキシを完全に迂回してHibernateに Session.get() を使って本物をロードするように依頼することもできます。

Employee finnigan = session.get(Employee.class, new Long(6));
  • これはプロキシを返すのではなく、すぐにデータベースを呼び出します。

  • 実際には、 ObjectNotFoundException の代わりに、 __ finniganが存在しない場合は null__を返します。

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

get() は便利ですが、 load() はデータベース上ではより軽量になります。

たとえば、 __gerald __が新会社に勤務するとしましょう。

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);

この状況では __employee recordのみを変更することを知っているので Companyのために load()を呼び出すことは賢明です。

Company get() を呼び出した場合は、そのすべてのデータをデータベースから不必要にロードしたことになります。**

=== 6. 結論

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

また、 load() が__get()とどう違うのかを簡単に見てみました。

いつも通り、チュートリアルに付随する完全なソースコードは 利用可能 https://github.com/eugenp/tutorials/tree/master/persistence-modules/hibernate5 [over GitHubで。