1. 序章

この短いチュートリアルでは、 @ org.hibernate.annotations.Fetchアノテーションで使用できるさまざまなFetchMode値を見ていきます。

2. 例の設定

例として、IDと一連の注文の2つのプロパティを持つ次のCustomerエンティティを使用します。

@Entity
public class Customer {

    @Id
    @GeneratedValue
    private Long id;

    @OneToMany(mappedBy = "customer")
    @Fetch(value = FetchMode.SELECT)
    private Set<Order> orders = new HashSet<>();

    // getters and setters
}

また、ID、名前、およびCustomerへの参照で構成されるOrderエンティティを作成します。

@Entity
public class Order {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;

    // getters and setters
}

次の各セクションでは、データベースから顧客を取得し、すべての注文を取得します。

Customer customer = customerRepository.findById(id).get();
Set<Order> orders = customer.getOrders();

3. FetchMode.SELECT

Customer エンティティで、注文プロパティに@Fetchアノテーションを付けました。

@OneToMany
@Fetch(FetchMode.SELECT)
private Set<Orders> orders;

@Fetch を使用して、Customer。を検索するときにHibernateがプロパティを取得する方法を説明します。

SELECT を使用すると、プロパティを遅延ロードする必要があることを示します。

これは、最初の行について次のことを意味します。

Customer customer = customerRepository.findById(id).get();

注文テーブルとの結合は表示されません。

Hibernate: 
    select ...from customer
    where customer0_.id=?

そしてそれは次の行のために:

Set<Order> orders = customer.getOrders();

関連する注文に対する後続のクエリが表示されます。

Hibernate: 
    select ...from order
    where order0_.customer_id=?

Hibernate FetchMode.SELECT は、ロードする必要のあるOrderごとに個別のクエリを生成します。

この例では、Customersをロードするための1つのクエリと、注文コレクションをロードするための5つの追加クエリを提供します。

これはn+1選択問題として知られています。1つのクエリを実行すると、nの追加クエリがトリガーされます。

3.1. @ BatchSize

FetchMode.SELECT には、@BatchSizeアノテーションを使用したオプションの構成アノテーションがあります。

@OneToMany
@Fetch(FetchMode.SELECT)
@BatchSize(size=10)
private Set<Orders> orders;

Hibernate は、sizeパラメーターで定義されたバッチで注文コレクションを読み込もうとします。

この例では、注文が5つしかないため、1つのクエリで十分です。

引き続き同じクエリを使用します。

Hibernate:
    select ...from order
    where order0_.customer_id=?

ただし、実行されるのは1回だけです。これで、クエリは2つだけになります。1つは Customer をロードし、もう1つは注文コレクションをロードします。

4. FetchMode.JOIN

FetchMode.SELECT はリレーションを遅延ロードしますが、 FetchMode.JOIN はそれらを熱心にロードします。たとえば、結合を介して:

@OneToMany
@Fetch(FetchMode.JOIN)
private Set<Orders> orders;

これにより、CustomerとそのOrderの両方に対して1つのクエリが実行されます。

Hibernate: 
    select ...
    from
        customer customer0_ 
    left outer join
        order order1 
            on customer.id=order.customer_id 
    where
        customer.id=?

5. FetchMode.SUBSELECT

orders プロパティはコレクションであるため、FetchMode.SUBSELECTを使用することもできます。

@OneToMany
@Fetch(FetchMode.SUBSELECT)
private Set<Orders> orders;

SUBSELECTはコレクションでのみ使用できます。

この設定では、 Customer:の1つのクエリに戻ります。

Hibernate: 
    select ...
    from customer customer0_

そして、今回は副選択を使用して、Orderの1つのクエリを実行します。

Hibernate: 
    select ...
    from
        order order0_ 
    where
        order0_.customer_id in (
            select
                customer0_.id 
            from
                customer customer0_
        )

6. FetchMode対。 FetchType

一般に、 FetchMode は、 Hibernate がデータをフェッチする方法(選択、結合、または副選択)を定義します。 一方、 FetchType は、Hibernateがデータを熱心にロードするか遅延してロードするかを定義します。

これら2つの間の正確なルールは次のとおりです。

  • コードがFetchModeを設定しない場合、デフォルトは JOIN であり、FetchTypeは定義どおりに機能します
  • FetchMode.SELECTまたはFetchMode.SUBSELECTが設定されている場合、FetchTypeも定義どおりに機能します
  • FetchMode.JOIN が設定されている場合、 FetchType は無視され、クエリは常に熱心になります

詳細については、Hibernateでの熱心な/遅延読み込みを参照してください。

7. 結論

このチュートリアルでは、 FetchMode のさまざまな値と、それらがFetchTypeとどのように関連しているかについて学習しました。

いつものように、すべてのソースコードはGitHub利用できます。