这是一个类似于: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
答案 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