我使用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找到了一个类似答案的相关问题:
答案 0 :(得分:0)
您应该考虑的一件事是您是否确实需要知道页数/元素总数。如果您从具有数百万个元素的结果集中返回页面,那么您的用户可能无论如何都无法查看所有这些页面:)。也许你的前端以无限卷轴显示数据,只需要知道,如果有更多页面,而不是页面数。
如果这些情况适用于您,您应该考虑返回Slice
而不是Page
,如:
public Slice<MyClass> findByMyField(..);
这样,Spring Data不再需要昂贵的Count
,而是只需要一个比你原先想要的元素多一个元素。如果该元素存在,Slice
将从hasNext
方法返回true。
在我工作的地方,我们最近将切片用于几个大型数据集并使用正确的索引(在clearing the database cache之后:)我们已经看到了一些非常显着的收益。
答案 1 :(得分:0)
我检查的EclipseLink 2.5源我相信它支持在以下数据库平台类中内置的数据库级别过滤:
每个方法都会覆盖printSQLSelectStatement方法,以利用它们各自的数据库功能来允许在SQL本身中进行过滤。其他平台需要使用JDBC过滤,这取决于驱动程序来限制行 - 它们可能能够优化查询,但它是特定于驱动程序的,我相信这就是为什么您的查询需要的时间比您想要的要长。
我不太清楚SQLServer是否足以说明它在SQL中可以使用的等效功能,但如果找到它,则需要创建一个SQLServerPlatform子类,覆盖printSQLSelectStatement方法,就像在上面的类,然后指定使用平台类。还请提交错误/功能以将其包含在EclipseLink中。
此处描述了其他选项: http://wiki.eclipse.org/EclipseLink/Examples/JPA/Pagination