【问题标题】:How to handle a large set of data using Spring Data Repositories?如何使用 Spring Data Repositories 处理大量数据?
【发布时间】:2013-02-20 07:52:21
【问题描述】:

我有一个大表,我想通过 Spring Data Repository 访问它。

目前,我正在尝试扩展PagingAndSortingRepository 接口,但似乎我只能定义返回列表的方法,例如:

public interface MyRepository extends 
        PagingAndSortingRepository<MyEntity, Integer>
{
  @Query(value="SELECT * ...")
  List<MyEntity> myQuery(Pageable p);
}

另一方面,PagingAndSortingRepository 附带的findAll() 方法返回一个Iterable(我假设数据没有加载到内存中)。

是否可以定义同时返回 Iterable 和/或不一次将所有数据加载到内存中的自定义查询?

有没有其他方法可以处理大表?

【问题讨论】:

  • List 实现了Iterable 接口,因此您的自定义查询方法确实返回Iterable
  • 我想当我使用 findAll() 时 Spring Data 不会将所有内容加载到内存中,我错了吗?我将编辑问题。
  • underlying implementation 只是检索一个列表,所以没有那么复杂。
  • 我明白了,谢谢你的解释。顺便问一下,你将如何解决这个问题?使用 Pageable 参数是不将所有内容加载到内存中的唯一方法吗?

标签: spring repository spring-data


【解决方案1】:

findAll()implementation 只是将所有实体的整个列表加载到内存中。它的Iterable 返回类型并不意味着它实现了某种数据库级别的游标处理。

另一方面,您的自定义 myQuery(Pageable) 方法将只加载一页的实体,因为生成的实现尊重其 Pageable 参数。您可以将其返回类型声明为PageList。在后一种情况下,您仍然会收到相同(受限)数量的实体,但不会收到 Page 额外携带的元数据。

因此,您基本上做了正确的事情,以避免在自定义查询中将所有实体加载到内存中。

请查看related documentation here

【讨论】:

【解决方案2】:

这里有经典的咨询答案:视情况而定。由于该方法的实现是特定于存储的,因此我们依赖于底层存储 API。对于 JPA,没有机会提供流式访问,因为 ….getResultList() 返回 List。因此,我们还将List 暴露给客户端,因为特别是 JPA 开发人员可能习惯于使用列表。所以对于 JPA,唯一的选择是使用分页 API。

对于像 Neo4j 这样的商店,我们支持流式访问,因为存储库在 CRUD 方法以及 finder 方法的执行上返回 Iterable

【讨论】:

  • 嗨奥利弗,感谢您的解释。我创建了一个 Iterable 来抽象分页的东西。如果您有时间,我将非常感谢有关代码/方法的任何反馈。再次感谢您:) gist.github.com/josericardo/5102304
  • 我一般建议不要做这样的事情。交付一个静默获取数据的包装器很容易,但您会遇到各种问题,因为您无法真正控制会话边界,因此您会遇到LazyLoadingException。访问Page 有什么问题,遍历内容并重复如果Page.hasNext()true?这至少表明您在存储库级别获得会话边界,除非您处于范围更广的事务中。
  • 感谢您的反馈和时间 :)
【解决方案3】:

我认为您正在寻找的是 Spring Data JPA 流。它为数据获取带来了显着的性能提升,尤其是在具有数百万条记录的数据库中。在您的情况下,您可以考虑多种选择

  1. 在内存中一次提取所有数据
  2. 每次都使用分页和阅读页面
  3. 使用 Apache Spark 之类的东西
  4. 使用 Spring Data JPA 流式传输数据

为了使 Spring Data JPA Stream 工作,我们需要修改我们的 MyRepository 以返回 Stream&lt;MyEntity&gt;,如下所示:

public interface MyRepository extends PagingAndSortingRepository<MyEntity, Integer> {
    @QueryHints(value = {
        @QueryHint(name = HINT_CACHEABLE, value = "false"),
        @QueryHint(name = READ_ONLY, value = "true")
    })
    @Query(value="SELECT * ...")
    Stream<MyEntity> myQuery();
}

在本例中,我们禁用二级缓存并提示 Hibernate 实体将是只读的。如果您的要求不同,请确保根据您的要求相应地更改这些设置。

【讨论】:

    猜你喜欢
    • 2013-08-09
    • 2015-02-07
    • 2023-03-23
    • 2018-01-21
    • 2017-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-14
    相关资源
    最近更新 更多