WARNING: firstResult/maxResults specified with collection fetch; applying in memory!

QueryTranslatorImpl

@Override
    public List list(SessionImplementor session, QueryParameters queryParameters)
            throws HibernateException {
        // Delegate to the QueryLoader...
        errorIfDML();

        final QueryNode query = (QueryNode) sqlAst;
        final boolean hasLimit = queryParameters.getRowSelection() != null && queryParameters.getRowSelection().definesLimits();
        final boolean needsDistincting = ( query.getSelectClause().isDistinct() || hasLimit ) && containsCollectionFetches();

        QueryParameters queryParametersToUse;
        if ( hasLimit && containsCollectionFetches() ) {
            LOG.firstOrMaxResultsSpecifiedWithCollectionFetch();
            RowSelection selection = new RowSelection();
            selection.setFetchSize( queryParameters.getRowSelection().getFetchSize() );
            selection.setTimeout( queryParameters.getRowSelection().getTimeout() );
            queryParametersToUse = queryParameters.createCopyUsing( selection );
        }
        else {
            queryParametersToUse = queryParameters;
        }

        List results = queryLoader.list( session, queryParametersToUse );

        if ( needsDistincting ) {
            int includedCount = -1;
            // NOTE : firstRow is zero-based
            int first = !hasLimit || queryParameters.getRowSelection().getFirstRow() == null
                        ? 0
                        : queryParameters.getRowSelection().getFirstRow();
            int max = !hasLimit || queryParameters.getRowSelection().getMaxRows() == null
                        ? -1
                        : queryParameters.getRowSelection().getMaxRows();
            List tmp = new ArrayList();
            IdentitySet distinction = new IdentitySet();
            for ( final Object result : results ) {
                if ( !distinction.add( result ) ) {
                    continue;
                }
                includedCount++;
                if ( includedCount < first ) {
                    continue;
                }
                tmp.add( result );
                // NOTE : ( max - 1 ) because first is zero-based while max is not...
                if ( max >= 0 && ( includedCount - first ) >= ( max - 1 ) ) {
                    break;
                }
            }
            results = tmp;
        }

        return results;
    }
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是在内存中对其进行了假分页的处理。
原文地址:https://www.cnblogs.com/yuyutianxia/p/7954897.html