Hibernate - フェッチ戦略の例

Hibernate –フェッチ戦略の例

Hibernateには、Hibernateで生成されたselectステートメントを最適化するフェッチ戦略がほとんどないため、可能な限り効率的です。 フェッチ戦略は、マッピング関係で宣言され、Hibernateが関連するコレクションとエンティティをフェッチする方法を定義します。

フェッチ戦略

4つのフェッチ戦略があります

1. fetch-“ join” =遅延読み込みを無効にし、常にすべてのコレクションとエンティティを読み込みます。
2。 fetch-“ select”(デフォルト)=すべてのコレクションとエンティティを遅延ロードします。
3。 batch-size =” N” =最大「N」個のコレクションまたはエンティティをフェッチします。Not record
4。 fetch-âsubœsubselect=コレクションをサブselectステートメントにグループ化します。

詳細な説明については、Hibernate documentationを確認できます。

フェッチ戦略の例

フェッチ戦略のデモの「1対多の関係」の例を次に示します。 在庫は多くの在庫日次レコードに属します。

XMLファイルでフェッチ戦略を宣言する例

...

    
        
            
                
            
            
        
    

アノテーションでフェッチ戦略を宣言する例

...
@Entity
@Table(name = "stock", catalog = "example")
public class Stock implements Serializable{
...
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
    @Cascade(CascadeType.ALL)
    @Fetch(FetchMode.SELECT)
        @BatchSize(size = 10)
    public Set getStockDailyRecords() {
        return this.stockDailyRecords;
    }
...
}

フェッチ戦略がHibernateが生成したSQLステートメントにどのように影響するかを調べてみましょう。

1. fetch =” select”または@Fetch(FetchMode.SELECT)

これがデフォルトのフェッチ戦略です。 関連するすべてのコレクションの遅延読み込みが可能になりました。 例を見てみましょう…

//call select from stock
Stock stock = (Stock)session.get(Stock.class, 114);
Set sets = stock.getStockDailyRecords();

//call select from stock_daily_record
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
      StockDailyRecord sdr = (StockDailyRecord) iter.next();
      System.out.println(sdr.getDailyRecordId());
      System.out.println(sdr.getDate());
}

出力

Hibernate:
    select ...from example.stock
    where stock0_.STOCK_ID=?

Hibernate:
    select ...from example.stock_daily_record
    where stockdaily0_.STOCK_ID=?

Hibernateは2つのselectステートメントを生成しました

1. ストックレコードを取得するステートメントを選択します–session.get(Stock.class, 114)
2。 関連するコレクションを選択します–sets.iterator()

2. fetch =” join”または@Fetch(FetchMode.JOIN)

「結合」フェッチ戦略は、関連するすべてのコレクションの遅延読み込みを無効にします。 例を見てみましょう…

//call select from stock and stock_daily_record
Stock stock = (Stock)session.get(Stock.class, 114);
Set sets = stock.getStockDailyRecords();

//no extra select
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
      StockDailyRecord sdr = (StockDailyRecord) iter.next();
      System.out.println(sdr.getDailyRecordId());
      System.out.println(sdr.getDate());
}

出力

Hibernate:
    select ...
    from
        example.stock stock0_
    left outer join
        example.stock_daily_record stockdaily1_
            on stock0_.STOCK_ID=stockdaily1_.STOCK_ID
    where
        stock0_.STOCK_ID=?

Hibernateは1つのselectステートメントのみを生成し、Stockの初期化時に関連するコレクションをすべて取得します。 –session.get(Stock.class, 114)

1. ステートメントを選択して、ストックレコードを取得し、その関連コレクションを外部結合します。

3. batch-size =” 10″または@BatchSize(size = 10)

この「バッチサイズ」フェッチ戦略は、多くのHibernate開発者によって常に誤解されています。 ここでmisunderstandの概念を見てみましょう…

Stock stock = (Stock)session.get(Stock.class, 114);
Set sets = stock.getStockDailyRecords();

for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
      StockDailyRecord sdr = (StockDailyRecord) iter.next();
      System.out.println(sdr.getDailyRecordId());
      System.out.println(sdr.getDate());
}

あなたの期待される結果は何ですか、これはコレクションから10レコードごとにフェッチされますか? 出力を参照してください
Output

Hibernate:
    select ...from example.stock
    where stock0_.STOCK_ID=?

Hibernate:
    select ...from example.stock_daily_record
    where stockdaily0_.STOCK_ID=?

バッチサイズはここでは何もしませんでした。バッチサイズが機能する方法ではありません。 この声明を参照してください。

バッチサイズフェッチ戦略では、コレクション内のレコードの数を定義しません。 代わりに、ロードするコレクションの数を定義します。

—この声明を覚えるまでN回繰り返します—

もう一つの例

別の例を見てみましょう。すべての在庫レコードとそれに関連する日次レコード(コレクション)を1つずつ印刷します。

List list = session.createQuery("from Stock").list();

for(Stock stock : list){

    Set sets = stock.getStockDailyRecords();

    for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
            StockDailyRecord sdr = (StockDailyRecord) iter.next();
            System.out.println(sdr.getDailyRecordId());
            System.out.println(sdr.getDate());
    }
}
バッチサイズのフェッチ戦略なし

出力

Hibernate:
    select ...
    from example.stock stock0_

Hibernate:
    select ...
    from example.stock_daily_record stockdaily0_
    where stockdaily0_.STOCK_ID=?

Hibernate:
    select ...
    from example.stock_daily_record stockdaily0_
    where stockdaily0_.STOCK_ID=?

Keep repeat the select statements....depend how many stock records in your table.

データベースに20の在庫レコードがある場合、Hibernateのデフォルトのフェッチ戦略は20 + 1の選択ステートメントを生成し、データベースにヒットします。

1. すべてのStockレコードを取得するステートメントを選択します。
2。 関連するコレクション
3を選択します。 関連するコレクション
4を選択します。 関連するコレクションを選択します
…。
21。 関連するコレクションを選択

生成されたクエリは効率的ではなく、深刻なパフォーマンスの問題を引き起こしました。

batch-size = ’10 ’フェッチング戦略を有効にしました

batch-size = ’10’が有効になっている別の例を見てみましょう。
Output

Hibernate:
    select ...
    from example.stock stock0_

Hibernate:
    select ...
    from example.stock_daily_record stockdaily0_
    where
        stockdaily0_.STOCK_ID in (
            ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
        )

これで、Hibernateはselectinステートメントを使用してコレクションをパーフェッチします。 20の在庫レコードがある場合、3つの選択ステートメントが生成されます。

1. すべてのStockレコードを取得するステートメントを選択します。
2。 Inステートメントを選択して、関連するコレクションを1回フェッチします(一度に10コレクション)
3。 Inステートメントを選択して、その関連コレクションを取得します(一度に次の10コレクション)

バッチサイズを有効にすると、選択ステートメントが21個の選択ステートメントから3個の選択ステートメントに簡素化されます。

4. fetch =” subselect”または@Fetch(FetchMode.SUBSELECT)

このフェッチ戦略は、サブSELECTステートメントで関連するすべてのコレクションを有効にします。 同じクエリをもう一度見てみましょう。

List list = session.createQuery("from Stock").list();

for(Stock stock : list){

    Set sets = stock.getStockDailyRecords();

    for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
            StockDailyRecord sdr = (StockDailyRecord) iter.next();
            System.out.println(sdr.getDailyRecordId());
            System.out.println(sdr.getDate());
    }
}

出力

Hibernate:
    select ...
    from example.stock stock0_

Hibernate:
    select ...
    from
        example.stock_daily_record stockdaily0_
    where
        stockdaily0_.STOCK_ID in (
            select
                stock0_.STOCK_ID
            from
                example.stock stock0_
        )

「subselect」を有効にすると、2つのselectステートメントが作成されます。

1. すべてのStockレコードを取得するステートメントを選択します。
2。 サブ選択クエリで関連するコレクションをすべて選択します。

結論

フェッチ戦略は非常に柔軟であり、Hibernateクエリを最適化するための非常に重要な調整ですが、間違った場所で使用すると、全体的な災害になります。