问题描述
这是一个类似于以下问题:HQL-分页的行标识符
我正在尝试使用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个实体(每个Messages都有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
解决方法
根据JPA 2.0规范的第3.8.6节“查询执行”,
应用setMaxResults的效果
或setFirstResult到涉及以下内容的查询
获取集合的联接是
未定义。
它因数据库而异,根据我的经验,结果是Hibernate通常在内存中而不是在数据库查询级别进行分页。
我通常要做的是使用一个单独的查询来获取所需对象的ID,然后通过访存联接将其传递到查询中。
, 我正在使用以下解决方案:
/**
* @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);
}
欢迎。
, 最有可能的是,如果您使用HQL创建自己的查询,则查询生成器方法将无法解析自定义hql查询并对其进行更改。因此,应将“ 4”语句放在HQL查询的末尾,然后绑定偏移量参数。
, 由于不针对命令实体的某些属性来过滤结果集,因此还可以避免执行SQL连接,并为message的命令配置延迟获取。如果没有加入,Hibernate将使用数据库分页功能。
但是,您必须关心N + 1 seletcs问题,即避免对每个延迟获取的命令属性进行一次选择。您可以通过在休眠映射中设置batch-size属性或在休眠设置中全局设置hibernate.default_batch_fetch_size属性来避免这种情况。
例如:如果您在一个Hibernate会话中获取了100个消息对象,并且将批处理大小设置为10,则当您首次调用消息对象的getCommands()时,Hibernate将获取10个不同消息对象的10个命令关联。查询数量减少到10,再加上原始的消息获取。
在这里看看:http://java.dzone.com/articles/hibernate-tuning-queries-using?page=0,1作者比较了不同的获取策略作为一个简单的示例
, 我们可以使用Query and Criteria接口实现分页:
使用查询界面的分页:
查询接口有两种分页方法。
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接口进行分页:
用于分页的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);
}
, 我认为您原来的例外情况是不正确的。
发生的事情是,Hibernate提取所有消息,然后在所有消息全部加载后返回所需的消息。
查询处理期间发生的情况是setFirstResult(calculateOffset(page,limitPerPage))转换为OFFSET而setMaxResults(limitPerPage)转换为LIMIT