はじめに
自分的には遅延ローディングをLazyLoadingに書いた方が可読性の良い文章に見えるn、以下LazyLodingと書きます。
概要
HibernateはLazyLoadingを提供するために、Proxyオブジェクト
を使います。
つまり、実際のEntityを呼び出すのではなく、必要な時だけEntityをロードすることで性能を最適化します。
Proxyオブジェクトとは?
Proxyオブジェクトは、元のオブジェクトの代理として機能するオブジェクトです。
つまり、代わりに動作してくれる偽物オブジェクトです。
LazyLodingをするためにはリレーションデータが必要な時までロードを後回ししないといけないです。
でも、リレーション関係Entityのフィールドにnullを入れるわけにもいけないので、HibernateはリレーションEntityにProxyオブジェクトを注入することで実際のオブジェクトが存在しているように動作します。
例
Userは複数のAddreessを持っていると仮定します。
Entity
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(fetch = FetchType.LAZY) // 遅延ローディング設定
private List<Address> addresses = new ArrayList<>();
}
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String city;
}
@OneToMany(fetch = FetchType.LAZY)
でLazyLoadingを設定します。
// SELECT * FROM user WHERE id = 1;
// Addressはロードしないが、Proxyオブジェクトが注入される。
User user = userRepository.findById(userId).orElseThrow();
// getAddresses()でAddressにアクセスする瞬間、Addressロードされます。(LazyLoading)
// Addressはこの時点でもProxyオブジェクトですが、実際のAddressEntityを参照します。
List<Address> addresses = user.getAddresses();
Proxyは実際Entityの継承オブジェクトだ!
Proxyはどうやって実際のオブジェクトのように動作できるのでしょうか?
Proxyが実際のオブジェクトを継承したタイプを持っているからです。
そのため、実際のオブジェクトへの参照を保持していて、Proxyオブジェクトのメソッドが呼ばれると、実際のオブジェクトのメソッドが呼ばれます。
なので!
AddressProxy
タイプであるはずなのにAddress
として使われても問題ないのです。
Jpa Entity定義で必ず従うこと2つ
Proxyオブジェクトが実際のオブジェクトの継承クラスであることで以下の2つは必ず守るべきです。
- デフォルトコンストラクタは
private
アクセス制限で宣言してはいけない - Entityクラスは
final
で定義してはいけない
Proxy生成時にsuper
を呼び出すことができず、Entityをfinal
として宣言すれば継承が不可能になるからです。