概括潛在的Hibernate性能問(wèn)題
學(xué)習(xí)Hibernate時(shí),經(jīng)常會(huì)遇到Hibernate性能問(wèn)題,這里將介紹Hibernate性能問(wèn)題的解決方法。
在使用Hibernate進(jìn)行分頁(yè)的過(guò)程中,如果你收到如下警告,那么這里就是一個(gè)潛在的Hibernate性能問(wèn)題點(diǎn):
WARNING: firstResult/maxResults specified with collection fetch; applying in memory!
出現(xiàn)這個(gè)警告的直接后果是:無(wú)論你想要看第幾頁(yè)的數(shù)據(jù),從Hibernate打印出的SQL來(lái)看它總是查詢了所有滿足條件的結(jié)果。這是為什么呢?來(lái)看看這句警告所在的代碼,它位于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 );
關(guān)鍵在于if ( hasLimit && containsCollectionFetches() 這句判斷,如果滿足了這個(gè)條件,RowSelection將會(huì)被重新生成,原本分頁(yè)需要的firstRow和maxRows屬性將會(huì)丟失,后面的數(shù)據(jù)庫(kù)分頁(yè)自然也無(wú)法進(jìn)行。Hibernate這么做的原因從代碼上也很容易理解,如果查詢需要限制條數(shù)(limit/offset)并且需要fetch結(jié)合對(duì)象,則重新生成RowSelection,進(jìn)一步解釋,就是當(dāng)一個(gè)實(shí)體(A)和另一個(gè)實(shí)體(B)是One-To-Many關(guān)系的時(shí)候,一個(gè)需要fetch的典型查詢語(yǔ)句是“select distinct a from A a left join fetch a.b”,由于1個(gè)A可能對(duì)應(yīng)多個(gè)B,這個(gè)時(shí)候數(shù)據(jù)庫(kù)查詢的結(jié)果條數(shù)和需要生成的A對(duì)象的條數(shù)可能不一致,所以無(wú)法利用數(shù)據(jù)庫(kù)層的分頁(yè)來(lái)實(shí)現(xiàn),因?yàn)槟阏嬲敕猪?yè)的是A而不是A left join B。出現(xiàn)這個(gè)警告就是提醒你這個(gè)查詢實(shí)際上是查詢了所有滿足條件的數(shù)據(jù),Hibernate是在內(nèi)存中對(duì)其進(jìn)行了假分頁(yè)的處理。
這樣,對(duì)于查詢結(jié)果比較多的情況無(wú)疑是一個(gè)Hibernate性能上的潛在威脅。碰到這樣的情況,將Many的查詢進(jìn)行分開(kāi)也是一種解決辦法。
【編輯推薦】