Hibernate性能:fetching策略

Hibernate有一些fetching策略,来优化Hibernate所生成的select语句,以尽可能地提高效率。在映射关系中声明fetching策略,定义Hibernate怎样获取其相关的集合和实体。
有四种fetching策略:        
  • fetch-“join”:禁用延迟加载,总是立即加载所有的集合和实体。
  • fetch-“select”(默认):延迟加载所有的集合和实体。
  • batch-size=”N”:获取上限为“N”个的集合或实体,没有记录。
  • fetch-“subselect”:将其集合组织到一个子查询语句中。
下面是一个“一对多关系”示例,用来演示fetching策略。一个Stock属于许多SockDailyRecord。
在XML映射文件中声明fetch策略:

  1.                               
  2.                                 
  3.                                     table="stock_daily_record" batch-size="10"  fetch="select">
  4.                                   
  5.                                     
  6.                                   
  7.                                   
  8.                                 
  9.                               
  10.   
复制代码
以标注的形式声明fetch策略:                     
  1. @Entity
  2.                           @Table(name =  "stock", catalog = "xuejava")
  3.                           public class Stock implements Serializable{
  4.                           ...
  5.                             @OneToMany(fetch  = FetchType.LAZY, mappedBy = "stock")
  6.                             @Cascade(CascadeType.ALL)
  7.                             @Fetch(FetchMode.SELECT)
  8.                             @BatchSize(size = 10)
  9.                             public  Set getStockDailyRecords() {
  10.                               return  this.stockDailyRecords;
  11.                             }
  12.                           ...
  13.                           }     
复制代码
下面我们来探讨fetch策略如何影响到Hibernate生成的SQL语句。
1、fetch=”select” 或  @Fetch(FetchMode.SELECT)                 
这是默认的fetch策略。这可以延迟加载所有相关的集合。请看示例代码:
  1. //call select from stock
  2.   Stock stock = (Stock)session.get(Stock.class, 114);
  3.   Set sets = stock.getStockDailyRecords();

  4.   //call select from stock_daily_record
  5.   for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
  6.     StockDailyRecord sdr = (StockDailyRecord) iter.next();
  7.     System.out.println(sdr.getDailyRecordId());
  8.     System.out.println(sdr.getDate());
  9.   }
复制代码

输出如下:
  1. Hibernate:
  2.   select ...from xuejava.stock
  3.   where stock0_.STOCK_ID=?

  4. Hibernate:
  5.   select ...from xuejava.stock_daily_record
  6.   where stockdaily0_.STOCK_ID=?
复制代码
Hibernate生成两个select语句:           
  • 获取Stock记录的select语句-session.get(Stock.class,114)
  • 获取其相关集合的select语句-sets.iterator()
2、fetch=”join” 或  @Fetch(FetchMode.JOIN)                                                   
“join”fetch策略将禁用延迟加载所有相关的集合。请看示例代码:
  1. //call select from stock and stock_daily_record
  2.   Stock stock = (Stock)session.get(Stock.class, 114);
  3.   Set sets = stock.getStockDailyRecords();

  4.   //no extra select
  5.   for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
  6.     StockDailyRecord sdr = (StockDailyRecord) iter.next();
  7.     System.out.println(sdr.getDailyRecordId());
  8.     System.out.println(sdr.getDate());
  9.   }
复制代码
输出如下:
  1. Hibernate:
  2.   select ...
  3.   from
  4.     xuejava.stock stock0_
  5.   left outer join
  6.     xuejava.stock_daily_record stockdaily1_
  7.     on stock0_.STOCK_ID=stockdaily1_.STOCK_ID
  8.   where
  9.     stock0_.STOCK_ID=?
复制代码
Hibernate只生成一个select语句,当Stock被初始化时,它获取所有其相关的集合。
3、batch-size=”10” 或  @BatchSize(size=10)  这个“batch-size”fetching策略总是被许多Hibernate开发者感到难以理解。请看示例代码:
  1. Stock stock = (Stock)session.get(Stock.class, 114);
  2.   Set sets = stock.getStockDailyRecords();

  3.   for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
  4.     StockDailyRecord sdr = (StockDailyRecord) iter.next();
  5.     System.out.println(sdr.getDailyRecordId());
  6.     System.out.println(sdr.getDate());
  7.   }
复制代码
 输出如下:
  1. Hibernate:
  2.   select ...from xuejava.stock
  3.   where stock0_.STOCK_ID=?

  4. Hibernate:
  5.   select ...from xuejava.stock_daily_record
  6.   where stockdaily0_.STOCK_ID=?                                                      
复制代码

batch-size什么也没做。请看下面的解释:

batch-size fetching策略并不是定义集合中有多少记录被预加载。相反,它其实是定义有多少集合应该被加载。                 
4、另一个示例                                             
接下来看一看另一个例子。假设想要打印输出所有的股票记录以及其相关的股票日记录(集合),one  by one。
  1. List list = session.createQuery("from Stock").list();

  2.   for(Stock stock : list){

  3.     Set sets = stock.getStockDailyRecords();

  4.     for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
  5.       StockDailyRecord sdr = (StockDailyRecord) iter.next();
  6.       System.out.println(sdr.getDailyRecordId());
  7.       System.out.println(sdr.getDate());
  8.     }
  9.   }                                                             
复制代码
1、没有batch-size  fetching策略时,输出:
  1. Hibernate:
  2.   select ...
  3.   from xuejava.stock stock0_

  4. Hibernate:
  5.   select ...
  6.   from xuejava.stock_daily_record stockdaily0_
  7.   where stockdaily0_.STOCK_ID=?

  8. Hibernate:
  9.   select ...
  10.   from xuejava.stock_daily_record stockdaily0_
  11.   where stockdaily0_.STOCK_ID=?

  12. 继续重复select语句....这取决于数据表中有多少条stock记录。
复制代码
如果在数据库中有20条股票记录,Hibernate默认的fetching策略将生成20+1个select语句,这将对数据库造成一定的冲击。
                 
  • 获取所有Stock记录的select语句
  • 获取其相关集合的select语句
  • 获取其相关集合的select语句
  • 获取其相关集合的select语句
  • ……
生成的查询非常没有效率,并引起一系列性能问题。
2、启用batch-size=”10” fetching策略时,输出:
  1. Hibernate:
  2.   select ...
  3.   from xuejava.stock stock0_

  4. Hibernate:
  5.   select ...
  6.   from xuejava.stock_daily_record stockdaily0_
  7.   where
  8.     stockdaily0_.STOCK_ID in (
  9.       ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
  10.     )
复制代码
现在,Hibernate将预提取集合,使用select  in语句。如果在数据库中有20条股票记录,Hibernate将生成3个select语句。
               
  • 获取所有Stock记录的select语句
  • 预提取其相关集合的select  in语句(一次10个集合)
  • 预提取其相关集合的select  in语句(接下来一次10个集合)
启用batch-size策略,将查询语句从21个select语句简化至3个select语句。

5、 fetch=”subselect” 或  @Fetch(FetchMode.SUBSELECT)
Fetching策略能够将其所有相关集合放在一个子select语句中。请看下面的查询:
  1. List list = session.createQuery("from Stock").list();

  2.   for(Stock stock : list){

  3.     Set sets = stock.getStockDailyRecords();

  4.     for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
  5.       StockDailyRecord sdr = (StockDailyRecord) iter.next();
  6.       System.out.println(sdr.getDailyRecordId());
  7.       System.out.println(sdr.getDate());
  8.     }
  9.   }
复制代码
启用“subselect”,它将生成两个select语句:            
  • 获取所有Stock记录的select语句;
  • 在一个子查询语句中获取其相关集合的语句。

6、结论                                            
Fetching策略非常具有弹性,是一个优化Hibernate查询的非常重要的技巧。不过如果用在错误的地方,那将会是一个灾难。

你可能感兴趣的:(hibernate)