Spring Data Repository - 分页大型数据集 (EclipseLink)

Spring Data Repository - Paging large data sets (EclipseLink)

我正在使用 Spring 数据和 EclipseLink JPA 对数据库结果集进行服务器端分页。我一切正常,我得到了预期的分页结果,但我注意到大型数据集(几百万行)的性能受到影响。 return 一页包含 20 个结果大约需要 5 分钟。也许这是意料之中的,但让我担心的是查询输出。

我的日志输出:

SELECT COUNT(filename) FROM document
SELECT filename, datecaptured, din, docdate, docid, doctype, drawer, foldernumber, format, pagenumber, tempfilename, userid FROM document ORDER BY din ASC

我明白为了分页,Spring 需要知道最大行数,所以第一个查询是有意义的。

第二个查询正在提取整个数据库,而我特别只要求 20 个偏移量为 0 的结果(页面)。


Spring/EclipseLink/JPA 是否实际上抓取了整个数据集,然后仅 return 分页请求的子集?

如果是这样,我应该如何修改我的存储库 class 以提高效率?

我的测试用例:

@Test
public void getPagedDocumentsTest() throws IOException {
    Page<Document> requestedPage = documentRepository.findAll(new PageRequest(0, 20, Sort.Direction.ASC, "din"));

    Assert.assertNotNull("Page is null", requestedPage);
    Assert.assertNotNull("Page is empty", requestedPage.getContent());

    List<Document> documents = requestedPage.getContent();

    LOG.info("{}", documents);
    LOG.info("{}", documents.size());
}

我的仓库class:

import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;

import com.example.data.model.Document;

@Repository
public interface DocumentRepository extends PagingAndSortingRepository<Document, String> {

}

编辑 - 根据@Chris 的建议

尝试将平台添加到我的属性中,但没有任何区别:

eclipselink.weaving=static
eclipselink.allow-zero-id=true
eclipselink.target-database=SQLServer
eclipselink.logging.level=FINE

还尝试将它添加到我的配置中(我正在使用 Java 配置):

@Bean
public LocalContainerEntityManagerFactoryBean entityManager() {
    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setPersistenceUnitName("ExampleUnit");
    factory.setPackagesToScan("com.example.data.model");

    EclipseLinkJpaVendorAdapter eclipseLinkVendorAdapter = new EclipseLinkJpaVendorAdapter();
    eclipseLinkVendorAdapter.setDatabase(Database.SQL_SERVER);
    eclipseLinkVendorAdapter.setDatabasePlatform("SQLServer");
    factory.setJpaVendorAdapter(eclipseLinkVendorAdapter);

    factory.setDataSource(dataSource());
    factory.setJpaProperties(jpaProperties());
    factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());

    return factory;
}

看起来平台设置正确。

[EL Config]: connection: 2015-08-06 12:04:05.691--ServerSession(686533955)--Connection(1896042043)--Thread(Thread[main,5,main])--connecting(DatabaseLogin(
    platform=>SQLServerPlatform
    user name=> ""
    connector=>JNDIConnector datasource name=>null
))

但都没有帮助。 SQL 查询输出也保持不变。

编辑

从@Chris 找到了一个具有类似答案的相关问题:

EclipseLink generated SQL doesn't include pagination

您应该考虑的一件事是您是否真的需要知道页数/元素总数。如果您 return 从包含数百万个元素的结果集中访问一个页面,您的用户很可能不会对以任何方式浏览所有这些页面感兴趣 :)。也许您的前端以无限滚动方式显示数据,只需要知道是否还有更多页面,而不是页面数。

如果这些情况中的任何一个适用于您,您应该考虑 returning Slice 而不是 Page,如:

public Slice<MyClass> findByMyField(..);

这样,Count、Spring 数据将只要求比您最初想要的多一个元素,而不是执行昂贵的操作。如果该元素存在,Slice 将从 hasNext 方法 return 为真。

在我工作的地方,我们最近将 Slices 用于几个大型数据集并使用正确的索引(在 clearing the database cache :) 之后,我们看到了一些非常显着的收益。

我检查过的 EclipseLink 2.5 源我相信支持内置于以下数据库平台的数据库级过滤 classes:

  • DB2 平台
  • 德比平台
  • 火鸟平台
  • H2平台
  • HANA 平台
  • HSQL平台
  • 我的SQL平台
  • Oracle 平台
  • PostgreSQL平台
  • SymfowarePlatform

这些中的每一个都覆盖了 printSQLSelectStatement 方法,以利用它们各自的数据库功能来允许在 SQL 本身中进行过滤。其他平台将需要使用 JDBC 过滤,这取决于驱动程序来限制行 - 它们可能能够优化查询,但它是特定于驱动程序的,我相信这就是为什么您的查询需要比您期望的时间更长的原因。

我不太了解 SQL服务器,无法说明它具有哪些可在 SQL 中使用的等效功能,但如果找到它,则需要创建一个 SQLServerPlatform subclass,重写 printSQLSelectStatement 方法,就像在上面 classes 中所做的那样,然后指定要使用的平台 class。还请提交 bug/feature 以将其包含在 EclipseLink 中。

此处描述了其他选项: http://wiki.eclipse.org/EclipseLink/Examples/JPA/Pagination