public List getPersonList(int max, int whichpage) {
try {
int index = (whichpage - 1) * max;
Query query = em.createQuery("from Person p order by personid asc");
List list = query.setMaxResults(max).setFirstResult(index).getResultList();
em.clear();// 分离内存中受EntityManager管理的实体bean,让VM进行垃圾回收
return list;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
上面是网上查到JPA分页的例子,发现JPA分页有2个缺陷
1. 类似于上例,用query.setFirstResult(5);query.setMaxResults(2)是想找第5--7条
   虽然结果正确,但看了log中的sql,竟然是select top 7 然后返回第6,7两条。而我们自己写sql则是可通过一定的写法直接返回第6.7条。 如果数据量大JPA且不是要查询装载很多额外的数据?
2. 用该方法查找出来的无法缓存?请问是这样吗,有没有更好的办法?
如例子中如果不用em.clear会有什么副作用?

解决方案 »

  1.   

    这个跟jpa没关系,跟数据库的支持,以及jpa实现有关系,比如hibernate,不同的数据库有不同的dialect,不同的dialect处理分页查询时会有不同的拼装sql的方式。如果数据库不支持更好的分页查询,jpa的实现也没辙。
    如果数据库支持了,但jpa的实现没使用拼装更好的sql,那是jpa的实现做的不够好。
      

  2.   


    你就单凭这样测试,就说jpa的分页效率底下?你是跟什么比?
      

  3.   


    100万比数据,你同时用jpa和hibernate,ibatis试试看
      

  4.   

    我用的是sqlserver,如果是直接写sql,可以通过 select top 2 from TB where id not in (select top 5)类似这样的语句,仅仅找出2条需要的记录。
    而不是像JPA那样先找出7个实体,然后在返回后面的2个,这里就多余了5个实体的装载。
    我说的效率低,是指多抓了数据,占时间,占内存
      

  5.   

    OK,用的sqlserver,让我们来看看hibernate3.3中SqlServerDialect是怎么处理分页的 public String getLimitString(String querySelect, int offset, int limit) {
    if ( offset > 0 ) {
    throw new UnsupportedOperationException( "query result offset is not supported" );
    }
    return new StringBuffer( querySelect.length() + 8 )
    .append( querySelect )
    .insert( getAfterSelectInsertPoint( querySelect ), " top " + limit )
    .toString();
    }可以看到使用的是top N的方式。
    让我们再来看看新的hibernate4.1.2中的dialect,发现多了2个sqlserverdialect:SQLServer2005Dialect,SQLServer2008Dialect。说明hibernate4针对sqlserver2005,2008做了增强。 /**
     * Add a LIMIT clause to the given SQL SELECT (HHH-2655: ROW_NUMBER for Paging)
     *
     * The LIMIT SQL will look like:
     *
     * <pre>
     * WITH query AS (
     *   SELECT ROW_NUMBER() OVER (ORDER BY orderby) as __hibernate_row_nr__,
     *   original_query_without_orderby
     * )
     * SELECT * FROM query WHERE __hibernate_row_nr__ BEETWIN offset AND offset + last
     * </pre>
     *
     * @param querySqlString The SQL statement to base the limit query off of.
     * @param hasOffset Is the query requesting an offset?
     *
     * @return A new SQL statement with the LIMIT clause applied.
     */
    @Override
    public String getLimitString(String querySqlString, boolean hasOffset) {
    StringBuilder sb = new StringBuilder( querySqlString.trim().toLowerCase() ); int orderByIndex = sb.indexOf( "order by" );
    CharSequence orderby = orderByIndex > 0 ? sb.subSequence( orderByIndex, sb.length() )
    : "ORDER BY CURRENT_TIMESTAMP"; // Delete the order by clause at the end of the query
    if ( orderByIndex > 0 ) {
    sb.delete( orderByIndex, orderByIndex + orderby.length() );
    } // HHH-5715 bug fix
    replaceDistinctWithGroupBy( sb ); insertRowNumberFunction( sb, orderby ); // Wrap the query within a with statement:
    sb.insert( 0, "WITH query AS (" ).append( ") SELECT * FROM query " );
    sb.append( "WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?" ); return sb.toString();
    }看到注释中有说明,使用了新的方式来做分页查询。所以,正如我上面所说的,数据库支持的时候,但jpa的实现可能没有做好。如果楼主用的hibernate4,就可以通过配置使用针对更新数据库的dialect:SQLServer2005Dialect或SQLServer2008Dialect
      

  6.   

    jap对db2的分页,有吗,给个,谢谢