从PageRequest中禁用计数查询以获取总页数的方法?

时间:2012-09-28 17:35:27

标签: spring spring-data spring-data-jpa

我们正在将Spring Data与PageRequest一起使用并点击大量数据。除了执行查询以获取总页数之外,所有查询都以最佳方式执行。有没有办法禁用此功能,还是我们很可能必须实现自己的Pageable?

http://static.springsource.org/spring-data/data-commons/docs/1.3.2.RELEASE/api/org/springframework/data/domain/PageRequest.html

http://static.springsource.org/spring-data/data-commons/docs/1.3.2.RELEASE/api/org/springframework/data/domain/Pageable.html

编辑:经过进一步分析后,我认为解决此问题的唯一方法是不使用Spring Data并使用EntityManager,因为它允许设置要返回的起始行和记录数。我们只需要下一页是否可用,所以我们只需要检索一条额外的记录。我们还需要一个动态查询,这在Spring Data中似乎是不可能的。

编辑2:而且似乎我没有等待足够长时间来回复某些问题。谢谢你们!

3 个答案:

答案 0 :(得分:21)

实现此目的的方法只是使用List作为返回值。例如,对于像这样定义的存储库:

interface CustomerRepository extends Repository<Customer, Long> {

  List<Customer> findByLastname(String lastname, Pageable pageable);
}

查询执行引擎将应用Pageable所传递的offset和pagesize,但不会触发附加计数查询,因为我们不需要构造Page实例。这也记录在参考文档的relevant sections中。

答案 1 :(得分:1)

我能够通过几篇文章中指出的基本存储库解决方案避免动态查询(使用 Spring Data Specifications)中的计数性能下降。

<?php
require_once __DIR__ . "/vendor/autoload.php";

use CoinbaseCommerce\ApiClient;
use CoinbaseCommerce\Resources\Charge;

/**
 * Init ApiClient with your Api Key
 * Your Api Keys are available in the Coinbase Commerce Dashboard.
 * Make sure you don't store your API Key in your source code!
 */

ApiClient::init("MY API KEY HERE");

$chargeObj = new Charge();

$chargeObj->name = 'Bitcoin Deposit';
$chargeObj->description = 'Testing the payment system';
$chargeObj->local_price = [
    'amount' => '100.00',
    'currency' => 'USD'
];
$chargeObj->pricing_type = 'fixed_price';

try {
    $chargeObj->save();

    // insert into database with status pending
    $queryobject->insertTransaction($_SESSION['user_id'],$chargeObj->id, $amount, $status, $currentTime, 
    $chargeObj->name, $chargeObj->currency);

} catch (\Exception $exception) {
    echo sprintf("Sorry! payment could not be created. Error: %s \n", $exception->getMessage());
}




if ($chargeObj->id) {
    $chargeObj->description = "New description";
    // Retrieve charge by "id"
    try {
        $retrievedCharge = Charge::retrieve($chargeObj->id);
        $hosted_url = $retrievedCharge->hosted_url;
        header('location: '.$hosted_url);
    } catch (\Exception $exception) {
        echo sprintf("Enable to retrieve charge. Error: %s \n", $exception->getMessage());
    }
}

使用这种方法从 6M 记录数据集中检索 20 个记录切片的查询需要毫秒。与 SQL 中运行的相同过滤查询略有不同。

使用 public class ExtendedRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements ExtendedRepository<T, ID> { private EntityManager entityManager; public ExtendedRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); this.entityManager = entityManager; } @Override public List<T> find(Specification<T> specification, int offset, int limit, Sort sort) { TypedQuery<T> query = getQuery(specification, sort); query.setFirstResult(offset); query.setMaxResults(limit); return query.getResultList(); } } 的类似实现需要 10 多秒。

返回 Slice<T> find(Specification<T> specification, Pageable pageable) 的类似实现大约需要 15 秒。

答案 2 :(得分:0)

我最近有这样的要求,最新的 spring-boot-starter-data-jpa 库提供了现成的解决方案。如果没有 count 功能,则可以使用 org.springframework.data.domain.Slice 界面实现分页。

blog的摘录

根据您在应用程序中使用的数据库,它可能随着项目数量的增加而变得昂贵。为避免此昂贵的计数查询,您应该返回一个Slice。与Page不同,Slice仅知道下一个切片是否可用。此信息足以遍历更大的结果集。 Slice和Page都是Spring Data JPA的一部分,其中Page只是Slice的子接口,带有一些其他方法。如果不需要项目和页面的总数,则应使用切片。

@Repository
public interface UserRepository extends CrudRepository<Employee, String> {

    Slice<Employee> getByEmployeeId(String employeeId, Pageable pageable);

}

使用Slice#hasNext示例代码片段浏览更大的结果集。在hasNext方法返回false之前,所请求的查询条件可能存在数据。

        int page = 0;
        int limit = 25;
        boolean hasNext;
        do {
            PageRequest pageRequest = PageRequest.of(page, limit );
            Slice<Employee> employeeSlice = employeeRepository.getByEmployeeId(sourceId, pageRequest);
            ++page;
            hasNext = employeeSlice .hasNext();
        } while (hasNext);