概括潛在的Hibernate性能問題
學習Hibernate時,經常會遇到Hibernate性能問題,這里將介紹Hibernate性能問題的解決方法。
在使用Hibernate進行分頁的過程中,如果你收到如下警告,那么這里就是一個潛在的Hibernate性能問題點:
WARNING: firstResult/maxResults specified with collection fetch; applying in memory!
出現這個警告的直接后果是:無論你想要看第幾頁的數據,從Hibernate打印出的SQL來看它總是查詢了所有滿足條件的結果。這是為什么呢?來看看這句警告所在的代碼,它位于org.hibernate.hql.ast.QueryTranslatorImpl中,部分摘錄如下:
- view plaincopy to clipboardprint?
- QueryNode query = ( QueryNode ) sqlAst;
- boolean hasLimit = queryParameters.getRowSelection() != null &&
queryParameters.getRowSelection().definesLimits();- boolean needsDistincting = ( query.getSelectClause().isDistinct() || hasLimit ) &&
containsCollectionFetches();- QueryParameters queryParametersToUse;
- if ( hasLimit && containsCollectionFetches() ) {
- log.warn( "firstResult/maxResults specified with collection fetch; applying in memory!" )
- RowSelection selection = new RowSelection();
- selection.setFetchSize( queryParameters.getRowSelection().getFetchSize() );
- selection.setTimeout( queryParameters.getRowSelection().getTimeout() );
- queryParametersqueryParametersToUse = queryParameters.createCopyUsing( selection );
- }
- else {
- queryParametersqueryParametersToUse = queryParameters;
- }
- List results = queryLoader.list( session, queryParametersToUse );
- QueryNode query = ( QueryNode ) sqlAst;
- boolean hasLimit = queryParameters.getRowSelection() != null &&
queryParameters.getRowSelection().definesLimits();- boolean needsDistincting = ( query.getSelectClause().isDistinct() || hasLimit ) &&
containsCollectionFetches();- QueryParameters queryParametersToUse;
- if ( hasLimit && containsCollectionFetches() ) {
- log.warn( "firstResult/maxResults specified with collection fetch; applying in memory!" );
- RowSelection selection = new RowSelection();
- selection.setFetchSize( queryParameters.getRowSelection().getFetchSize() );
- selection.setTimeout( queryParameters.getRowSelection().getTimeout() );
- queryParametersqueryParametersToUse = queryParameters.createCopyUsing( selection );
- }
- else {
- queryParametersqueryParametersToUse = queryParameters;
- }
- List results = queryLoader.list( session, queryParametersToUse );
關鍵在于if ( hasLimit && containsCollectionFetches() 這句判斷,如果滿足了這個條件,RowSelection將會被重新生成,原本分頁需要的firstRow和maxRows屬性將會丟失,后面的數據庫分頁自然也無法進行。Hibernate這么做的原因從代碼上也很容易理解,如果查詢需要限制條數(limit/offset)并且需要fetch結合對象,則重新生成RowSelection,進一步解釋,就是當一個實體(A)和另一個實體(B)是One-To-Many關系的時候,一個需要fetch的典型查詢語句是“select distinct a from A a left join fetch a.b”,由于1個A可能對應多個B,這個時候數據庫查詢的結果條數和需要生成的A對象的條數可能不一致,所以無法利用數據庫層的分頁來實現,因為你真正想分頁的是A而不是A left join B。出現這個警告就是提醒你這個查詢實際上是查詢了所有滿足條件的數據,Hibernate是在內存中對其進行了假分頁的處理。
這樣,對于查詢結果比較多的情況無疑是一個Hibernate性能上的潛在威脅。碰到這樣的情況,將Many的查詢進行分開也是一種解決辦法。
【編輯推薦】