为了账号安全,请及时绑定邮箱和手机立即绑定

来自 PostgreSQL 的流行(具有获取大小)

来自 PostgreSQL 的流行(具有获取大小)

小怪兽爱吃肉 2022-11-02 17:10:45
我想从 PostgreSQL 11.2 流式传输结果,而不是一次将所有结果读取到内存中。我使用最新的稳定版 SpringBoot 2.1.4.RELEASE。我阅读了如何在 MySQL 中执行此操作的文章。 http://knes1.github.io/blog/2015/2015-10-19-streaming-mysql-results-using-java8-streams-and-spring-data.html 我还阅读了如何在 PostgreSQL 中执行此操作的文章: Java 8 JPA 存储库在 Postgresql 中逐行流式传输我有这样的存储库:public interface ProductRepository extends JpaRepository<Product, UUID> {    @Query("SELECT p from Product p")    @QueryHints(value = @QueryHint(name = HINT_FETCH_SIZE, value = "50"))    Stream<Product> streamAll();}比我这样使用流:  productRepository.streamAll().forEach(product -> export(product));为了使示例更简单,'export' 方法是完全空的。当我调用该方法时,我看到了 Hibernate 查询Hibernate: select product0_.id as id1_0_, product0_.created as created2_0_, product0_.description as descript3_0_, product0_.name as name4_0_, product0_.product_type_id as product_5_0_ from products product0_ order by product0_.id一段时间后,我有 OutOfMemoryError。查询提示没有帮助。如何使用 Spring Boot 存储库(甚至 EntityManager)读取数据并以最佳方式从 DB 加载行。我知道我可以进行分页,但正如文章中所写的那样,这不是最理想的方式。
查看完整描述

3 回答

?
白衣染霜花

TA贡献1796条经验 获得超10个赞

您必须在工作完成后分离实体。


import javax.persistence.EntityManager;

...

@Autowired

private EntityManager entityManager;

... 

// Your business logic

productRepository.streamAll().forEach(product -> {

   export(product);

   // must detach so that garbage collector can reclaim the memory.

   entityManager.detach(product);

});


查看完整回答
反对 回复 2022-11-02
?
天涯尽头无女友

TA贡献1831条经验 获得超9个赞

目前使用 spring 检索所有数据,并且 Stream 仅应用于内存中已经存在的数据。

如果您查看它的来源,org.springframework.data.jpa.provider.PersistenceProvider它似乎使用 aScrollableResults来流式传输数据。

通常ScrollableResults检索内存中的所有数据。

您可以在此处找到使用 MySql 数据库的有趣的完整分析,但对于 Postgres 数据库可能同样适用。

因此,如果您认为使用实际上不需要使用大量内存的解决方案,它也会这样做,因为底层实现没有使用最佳实现。


查看完整回答
反对 回复 2022-11-02
?
陪伴而非守候

TA贡献1757条经验 获得超8个赞

我遇到了完全相同的问题,经过对 Spring Data 和 Hibernate 内部的长时间调试,找到了对我有用的解决方案。

因此,要在 PostgreSQL 中使用游标获取数据,它应该是带有 Stream 结果 + 注释的方法(kotlin 语法):

@QueryHints(QueryHint(name = org.hibernate.annotations.QueryHints.FETCH_SIZE, value = "50"))

哪个值应该是 50 或其他值 - 这并不重要。可能您输入了错误的提示名称。


查看完整回答
反对 回复 2022-11-02
  • 3 回答
  • 0 关注
  • 82 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信