Hibernate - HQL分页

时间:2011-06-14 18:19:52

标签: hibernate pagination hql

这是一个类似于:HQL - row identifier for pagination

的问题

我正在尝试使用HQL实现分页。我有一个PostgreSQL数据库。

int elementsPerBlock = 10;
int page = 2; //offset = 2*10

String sqlQuery = "FROM Messages AS msg " +
                  " LEFT JOIN FETCH msg.commands AS cmd " +   
                  "ORDER BY msg.identifier ASC" ;

Query query = session.createQuery( sqlQuery )
                     .setFirstResult( elementsPerBlock * ( (page-1) +1 ) )
                     .setMaxResults( elementsPerBlock );

Hibernate会抓取所有消息,并在它们全部加载后返回所需的消息。

因此,Hibernate获取210000个实体而不是30个实体(每个消息只有2个命令)。

有没有办法将开销减少7000倍?

编辑:我尝试添加.setFetchSize( elementsPerBlock )。它没有帮助。

编辑2:生成的SQL查询是:

select ... 
from schemaName.messages messages0_ 
left outer join schemaName.send_commands commands1_ 
on messages0_.unique_key=commands1_.message_key 
order by messages0_.unique_identifier ASC

绝对没有LIMIT或OFFSET

6 个答案:

答案 0 :(得分:15)

根据JPA 2.0 specification,第3.8.6节“查询执行”,

  

应用setMaxResults的效果   或setFirstResult到涉及的查询   在集合上获取联接是   未定义。

它因数据库而异,根据我的经验,结果是Hibernate通常在内存中而不是在数据库查询级别进行分页。

我通常使用单独的查询来获取所需对象的ID,并使用fetch join将其传递给查询。

答案 1 :(得分:7)

我正在使用这个解决方案:

/**
 * @param limitPerPage
 * @param page
 * @return
 */
public List<T> searchByPage(int limitPerPage, int page, String entity) {
    String sql = "SELECT t FROM " + entity + " t";
    Query query = em.createQuery(sql)
            .setFirstResult(calculateOffset(page, limitPerPage))
            .setMaxResults(limitPerPage);
    return query.getResultList();
}

/**
 * @param page
 * @return
 */
private int calculateOffset(int page, int limit) {
    return ((limit * page) - limit);
}

欢迎。

答案 2 :(得分:1)

最有可能的是,如果使用HQL创建自己的查询,查询构建器方法无法解析自定义hql查询并对其进行更改。因此,您应该将LIMIT ?, ?语句放在HQL查询的末尾,然后绑定偏移参数。

答案 3 :(得分:1)

由于您没有针对 command 实体的某些属性过滤结果集,因此您还可以避免SQL连接并为 message 的命令配置延迟提取。如果没有join,Hibernate将使用数据库分页功能。

但是,您必须关心 N + 1 seletcs问题,即避免对每个延迟提取的命令属性进行单一选择。您可以通过在hibernate映射中设置批量大小属性或在休眠设置中全局设置 hibernate.default_batch_fetch_size 属性来避免这种情况。

例如:如果您在Hibernate会话中获取了100个消息对象并设置了批量大小 10,则Hibernate将获取10个命令消息对象的 getCommands()时,em> 10个不同消息对象的关联。查询数量减少到10加上原始消息获取的数量。

看看这里:http://java.dzone.com/articles/hibernate-tuning-queries-using?page=0,1作者比较了一个简单例子的不同获取策略

答案 4 :(得分:1)

我们可以使用 查询和标准 界面来实现分页:

使用查询界面进行分页:

查询界面有两种分页方法。

<强> 1。查询setFirstResult(int startPosition): 此方法采用一个整数,表示结果集中的第一行,从第0行开始。

<强> 2。查询setMaxResults(int maxResult): 此方法告诉Hibernate检索固定数量的maxResults对象。将上述两种方法结合使用,我们可以在Web或Swing应用程序中构建一个分页组件。

示例:

Query query = session.createQuery("FROM Employee");
query.setFirstResult(5);
query.setMaxResults(10);
List<Employee> list = query.list();
for(Employee emp: list) {            
   System.out.println(emp);
}

使用条件界面进行分页:

分页的Criteria接口有两种方法。

<强> 1。标准setFirstResult(int firstResult):

设置要检索的第一个结果。

<强> 2。列表项标准setMaxResults(int maxResults):

设置要检索的对象数量的限制。

示例:

Criteria criteria = session.createCriteria(Employee.class);
criteria.setFirstResult(5);
criteria.setMaxResults(10);            
List<Employee> list = criteria.list();
for(Employee emp: list) {            
    System.out.println(emp);
}

答案 5 :(得分:0)

我认为您原来的例外情况不正确。

  

发生的事情是,Hibernate提取所有消息,并在所有消息全部加载后返回所需的消息。

查询处理时发生的情况是setFirstResult(calculateOffset(page,limitPerPage))转换为OFFSET,而setMaxResults(limitPerPage)转换为LIMIT