我们正在将Spring Data与PageRequest一起使用并点击大量数据。除了执行查询以获取总页数之外,所有查询都以最佳方式执行。有没有办法禁用此功能,还是我们很可能必须实现自己的Pageable?
编辑:经过进一步分析后,我认为解决此问题的唯一方法是不使用Spring Data并使用EntityManager,因为它允许设置要返回的起始行和记录数。我们只需要下一页是否可用,所以我们只需要检索一条额外的记录。我们还需要一个动态查询,这在Spring Data中似乎是不可能的。
编辑2:而且似乎我没有等待足够长时间来回复某些问题。谢谢你们!
答案 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);