[Hibernate] fetching strategy

Hibernate có một vài fetching strategy để tối ưu câu lệnh select được được tạo, để nó có hiệu quả nhất có thể. Fetching strategy được khai báo trong mapping relationship để định nghĩa cách Hibernate lấy các collection và entity iên quan của entity.


Các Fetching Strategy
Có 4 fetching stratery

1. fetch-“join” = Disable lazy loading, thường load tất cả các collection và entity liên quan.
2. fetch-“select” (mặc định) = Lazy load tất cả các collection và entity liên quan.
3. batch-size=”N” = Load ‘N’ collections hoặc entities, *Not record*.
4. fetch-“subselect” = Group collection trong lệnh sub select.

Chi tiết hơn, có thể xem tại Hibernate documentation.


Ví dụ về Fetching strategy
Đây là ví dụ về “one-to-many relationship” để trình bày về fetching strategy. Một stock có liên quan đến nhiều stock daily record.

Ví dụ khai báo fetch strategy trong file XML
XML:
...
<hibernate-mapping>
    <class name="com.mkyong.common.Stock" table="stock">
        <set name="stockDailyRecords"  cascade="all" inverse="true"
            table="stock_daily_record" batch-size="10" fetch="select">
            <key>
                <column name="STOCK_ID" not-null="true" />
            </key>
            <one-to-many class="com.mkyong.common.StockDailyRecord" />
        </set>
    </class>
</hibernate-mapping>
Ví dụ khai báo fetch strategy bằng annotation
Java:
...
@Entity
@Table(name = "stock", catalog = "mkyong")
public class Stock implements Serializable{
...
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
    @Cascade(CascadeType.ALL)
    @Fetch(FetchMode.SELECT)
        @BatchSize(size = 10)
    public Set<StockDailyRecord> getStockDailyRecords() {
        return this.stockDailyRecords;
    }
...
}
Bắt đầu khám phá fetch strategy ảnh hưởng như thế nào đến lệnh SQL do Hibernate tạo ra.


1. fetch=”select” hoặc @Fetch(FetchMode.SELECT)
Đây là fetching strategy mặc định. Nó enable lazy loading tất cả collection liên quan trong entity. Xem ví dụ bên dưới...

Java:
//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());
}
Kết quả
Java:
Hibernate:
    select ...from mkyong.stock
    where stock0_.STOCK_ID=?

Hibernate:
    select ...from mkyong.stock_daily_record
    where stockdaily0_.STOCK_ID=?
Hibernate tạo ra 2 lệnh select.

1. Lệnh Select để get ra entity Stock –session.get(Stock.class, 114)
2. Lệnh Select để get ra collection liên quan – sets.iterator()


2. fetch=”join” hoặc @Fetch(FetchMode.JOIN)
Fetching strategy “join” sẽ disable lazy loading của các collection liên quan. Xem ví dụ bên duưới...

Java:
//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());
}
Kết quả
Code:
Hibernate:
    select ...
    from
        mkyong.stock stock0_
    left outer join
        mkyong.stock_daily_record stockdaily1_
            on stock0_.STOCK_ID=stockdaily1_.STOCK_ID
    where
        stock0_.STOCK_ID=?
Hibernate chỉ tạo ra 1 lệnh select duy nhất, nó lấy ra tất cả collection liên quan khi tạo ra entity Stock.session.get(Stock.class, 114)
1. Lệnh select lấy ra entity Stock và outer join các collection liên quan của nó.


3. batch-size=”10″ hoặc @BatchSize(size = 10)
Fetching strategy ‘batch size’ thường bị hiểu nhầm bởi Hibernate developer. Xem điều này ở bên dưới..

Java:
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());
}
Kết quả kì vọng, là mỗi lần lấy 10 entity liên quan từ collection? Xem kết quả xuất ra
Kết quả

Code:
Hibernate:
    select ...from mkyong.stock
    where stock0_.STOCK_ID=?

Hibernate:
    select ...from mkyong.stock_daily_record
    where stockdaily0_.STOCK_ID=?
batch-size không có gì ở đây, nó không chạy theo batch-size. Xem câu này.

Fetching strategy batch-size không được định nghĩa để load bao nhiêu phần từ vào collection, Thay vào đó, nó định nghĩa làm thế nào để load được nhiều collection.
— Lặp lại N lần cho đến khi bạn nhớ câu này-
Ví dụ khác
Xem ví dụ khác, ta muốn in ra tất cả các entity stock và các stock daily record có liên quan (collections) từng dòng một.

Java:
List<Stock> 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());
    }
}
Không sử dụng fetching strategy batch-size
Kết quả
Code:
Hibernate:
    select ...
    from mkyong.stock stock0_

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

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

Keep repeat the select statements....depend how many stock records in your table.
Nếu có 20 dòng stock trong cơ sở dữ liệu, fetching strategy mặc định của hibernate sẽ tạo ra 20+1 lệnh select và chạy nó tại database.

1.Lệnh select lấy ra tất cả Stock.
2. .Lệnh select lấy ra tất cả các collection lên quan
3. Lệnh select lấy ra tất cả các collection lên quan
4. Lệnh select lấy ra tất cả các collection lên quan
….
21. Lệnh select lấy ra tất cả các collection lên quan

Query được tạo ra không hiệu quả và gây ra ảnh hường nghiêm trọng đến performance .

Enable fetching strategy batch-size=’10’
Xem ví dụ khác khi batch-size=’10’ được được enable.

Kết quả
Code:
Hibernate:
    select ...
    from mkyong.stock stock0_

Hibernate:
    select ...
    from mkyong.stock_daily_record stockdaily0_
    where
        stockdaily0_.STOCK_ID in (
            ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
        )
Bây giờ, Hibernate sẽ lần lượt load một số collection, với mỗi lệnh select *in*. Nếu có 20 dòng stock, nó sẽ tạo ra 3 lệnh select.

1.Lệnh select lấy ra tất cả Stock.
2. .Lệnh select lấy ra một số collection lên quan (10 collection trong 1 lần)
3. .Lệnh select lấy ra một số collection lên quan (10 collection tiếp theo)

Khi enable batch-size, nó giúp giảm số lượng lệnh select từ 21 lệnh xuống 3.


4. fetch=”subselect” hoặc @Fetch(FetchMode.SUBSELECT)
Fetching strategy này enable tất cả collection có liên quan trong một sub select. Hãy xem lại query tương tự một lần nữa.

Java:
List<Stock> 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());
    }
}
Kết quả
Code:
Hibernate:
    select ...
    from mkyong.stock stock0_

Hibernate:
    select ...
    from
        mkyong.stock_daily_record stockdaily0_
    where
        stockdaily0_.STOCK_ID in (
            select
                stock0_.STOCK_ID
            from
                mkyong.stock stock0_
        )
Với “subselect” được enable, nó sẽ tạo ra 2 lệnh select.

1. Select lấy ra tất cả các Stock.
2. Select lấy ra tất cả collection liên quan trong 1 sub select query.


Kết luận
Các fetching strategy có tính linh hoạt cao và là một tinh chỉnh rất quan trọng để tối ưu hóa query trong Hibernate, nhưng nếu sử dụng nó sai chỗ, sẽ là một thảm họa toàn diện.


Cám ơn các bạn đã theo dõi. Hẹn gặp lại các bạn trong các bài viết sau :D

Bài viết tham khảo tại: https://mkyong.com/hibernate/hibernate-fetching-strategies-examples/
 

Bình luận