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

时间:2015-08-05 18:55:19

标签: spring jpa pagination eclipselink spring-data

我使用Spring Data和EclipseLink JPA对数据库结果集进行服务器端分页。我有一切工作,我得到了预期的分页结果,但我注意到大型数据集(数百万行)的性能受到影响。返回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实际上是抓取整个数据集然后只返回子集分页请求吗?

如果是这种情况,我应该如何修改我的存储库类以提高效率?

我的测试用例:

@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());
}

我的存储库类:

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 Config):

@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

2 个答案:

答案 0 :(得分:0)

您应该考虑的一件事是您是否确实需要知道页数/元素总数。如果您从具有数百万个元素的结果集中返回页面,那么您的用户可能无论如何都无法查看所有这些页面:)。也许你的前端以无限卷轴显示数据,只需要知道,如果有更多页面,而不是页面数。

如果这些情况适用于您,您应该考虑返回Slice而不是Page,如:

public Slice<MyClass> findByMyField(..);

这样,Spring Data不再需要昂贵的Count,而是只需要一个比你原先想要的元素多一个元素。如果该元素存在,Slice将从hasNext方法返回true。

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

答案 1 :(得分:0)

我检查的EclipseLink 2.5源我相信它支持在以下数据库平台类中内置的数据库级别过滤:

  • DB2Platform
  • DerbyPlatform
  • FirebirdPlatform
  • H2Platform
  • HANAPlatform
  • HSQLPlatform
  • MySQLPlatform
  • OraclePlatform
  • PostgreSQLPlatform
  • SymfowarePlatform

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

我不太清楚SQLServer是否足以说明它在SQL中可以使用的等效功能,但如果找到它,则需要创建一个SQLServerPlatform子类,覆盖printSQLSelectStatement方法,就像在上面的类,然后指定使用平台类。还请提交错误/功能以将其包含在EclipseLink中。

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