当前位置: 首页 > 知识库问答 >
问题:

流式查询结果过早关闭--Spring Data、JPA和Hibernate

梅修贤
2023-03-14

下面是一个存储库,其中包含了这个问题中的代码来排除bug:https://github.com/agsimeonov/stream-bug

我一直试图用Spring Data、JPA和Hibernate使用以下代码(Data.txt是一个有3000行的文件,每行都有一个数字)流式传输查询结果:

try (Stream<Customer> stream = repository.streamAll()) {
  stream.forEach(customer -> {
    try {
      File data = new File(getClass().getClassLoader().getResource("data.txt").getFile());
      try (BufferedReader reader = new BufferedReader(new FileReader(data))) {
        while (reader.readLine() != null) {
          // Do stuff for the current customer
        }
      }
    } catch (IOException e) {}
    System.out.println(customer);
  });
}

下面是域对象:

@Entity
@Table(name = "customer")
public class Customer {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  private String firstName;
  private String lastName;

  public Customer() {}

  public Customer(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  @Override
  public String toString() {
    return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
  }
}
public interface CustomerRepository extends JpaRepository<Customer, Long> {
  @Query("SELECT c FROM Customer c")
  Stream<Customer> streamAll();
}
org.hibernate.exception.GenericJDBCException: could not advance using next()
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95)
    at org.hibernate.internal.ScrollableResultsImpl.convert(ScrollableResultsImpl.java:69)
    at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:104)
    at org.springframework.data.jpa.provider.PersistenceProvider$HibernateScrollableResultsIterator.hasNext(PersistenceProvider.java:454)
    at java.util.Iterator.forEachRemaining(Iterator.java:115)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
    at stream.bug.StreamBugApplication.lambda$0(StreamBugApplication.java:34)
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800)
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:784)
    at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:771)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1186)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1175)
    at stream.bug.StreamBugApplication.main(StreamBugApplication.java:22)
Caused by: org.h2.jdbc.JdbcSQLException: The object is already closed [90007-193]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
    at org.h2.message.DbException.get(DbException.java:179)
    at org.h2.message.DbException.get(DbException.java:155)
    at org.h2.message.DbException.get(DbException.java:144)
    at org.h2.jdbc.JdbcResultSet.checkClosed(JdbcResultSet.java:3202)
    at org.h2.jdbc.JdbcResultSet.next(JdbcResultSet.java:129)
    at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:99)
    ... 12 more

我肯定知道几件事:

首先-此bug与底层数据库无关。当我在使用Postgres的示例项目中使用H2时,仍然会出现非常类似的错误,注意我在另一个项目中使用tomcat连接池,我尝试了不同的连接池,所以肯定不是连接池或底层数据库造成的。下面是一个使用postgres和tomcat连接池的示例跟踪,您可能会注意到它非常相似:

org.hibernate.exception.GenericJDBCException: could not advance using next()
  at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
  at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
  at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
  at org.hibernate.internal.ScrollableResultsImpl.convert(ScrollableResultsImpl.java:69)
  at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:104)
  at org.springframework.data.jpa.provider.PersistenceProvider$HibernateScrollableResultsIterator.hasNext(PersistenceProvider.java:454)
  at java.util.Iterator.forEachRemaining(Iterator.java:115)
  at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
  at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
  at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
  at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
  at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
  at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
  at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
  at com.trove.sunstone.attributefusion.services.impl.PhysicalServiceImpl.match(PhysicalServiceImpl.java:130)
  at com.trove.sunstone.attributefusion.AppRunner.main(AppRunner.java:31)
  Suppressed: java.lang.reflect.UndeclaredThrowableException
    at com.sun.proxy.$Proxy238.hashCode(Unknown Source)
    at java.util.HashMap.hash(HashMap.java:338)
    at java.util.HashMap.get(HashMap.java:556)
    at org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl.release(ResourceRegistryStandardImpl.java:76)
    at org.hibernate.internal.AbstractScrollableResults.close(AbstractScrollableResults.java:104)
    at org.springframework.data.jpa.provider.PersistenceProvider$HibernateScrollableResultsIterator.close(PersistenceProvider.java:465)
    at org.springframework.data.util.StreamUtils$CloseableIteratorDisposingRunnable.run(StreamUtils.java:96)
    at java.util.stream.AbstractPipeline.close(AbstractPipeline.java:323)
    at com.trove.sunstone.attributefusion.services.impl.PhysicalServiceImpl.match(PhysicalServiceImpl.java:137)
    ... 1 more
  Caused by: java.sql.SQLException: Statement closed.
    at org.apache.tomcat.jdbc.pool.interceptor.AbstractQueryReport$StatementProxy.invoke(AbstractQueryReport.java:224)
    ... 10 more
Caused by: org.postgresql.util.PSQLException: This ResultSet is closed.
  at org.postgresql.jdbc.PgResultSet.checkClosed(PgResultSet.java:2740)
  at org.postgresql.jdbc.PgResultSet.next(PgResultSet.java:1817)
  at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:99)
  ... 11 more

其次,奇怪的是,从流中的forEach()中删除以下行会导致流正确地完成。这使我相信这可能是某种时间问题,但我尝试用thread.sleep()复制它,而不是读取文件,但没有成功。顺便说明一下,data.txt是一个有3000行的文件,每行都有一个数字。

try {
  File data = new File(getClass().getClassLoader().getResource("data.txt").getFile());
  try (BufferedReader reader = new BufferedReader(new FileReader(data))) {
    while (reader.readLine() != null) {
      // Do stuff for the current customer
    }
  }
} catch (IOException e) {}
Stream<Customer> stream = repository.streamAll()
Stream<Customer> stream = repository.findAll().stream()

修复了这个问题,所以这肯定是一个流和/或ScrollableResults的bug,因为将所有数据加载到列表中会使应用程序完成时没有错误,但是对于我当前的项目,我需要直接使用流,所以使用findAll()不是一个选项。

如果有人遇到了这个问题,并能够解决它,请让我知道。此外,请随时检查、分叉和/或更改提供的存储库中的代码,这有助于解决此问题。我已经创建了这个项目,作为一个演示,应该用来说明bug。

共有1个答案

贺功
2023-03-14

我在Spring Data JPA JIRA上发布了我的问题作为bug报告,这个问题显然以前就被观察到了。在对此进行了一些讨论之后,我现在在与流相关的代码上使用@Transactional来解决这个问题,作为一种变通方法。感谢Oliver Gierke在这里指出这一点:https://jira.spring.io/browse/datajpa-989?focusedcommentid=133710&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-133710

我已经将最新提交中的bug解决方案推送到我的示例bug存储库中:https://github.com/agsimeonov/stream-bug/commit/9da536d0a9d921787f6d2d4d75720d363ba0358b

 类似资料:
  • 我得到了以下数据结构 我正在寻找一个查询dsl谓词,它给我的所有出版物,其中任何Author.name包含某个字符串,例如“汉斯” 我试过了: 但是,如果有多个作者在名称中包含“Hans”,则这是抱怨。有没有像出版一样就像一个集合?

  • 我正在开发一个人力资源管理应用程序,所以我对如何通过JPA管理实体感到困惑。 我的情况是一组多语言上下文中的表:-employees-departments-languages-departments_languages 在我的数据库表之后: 从这个查询中,我需要员工信息,以及部门名称(假设languageId为1) 从eclipse JPA控制台执行查询将返回一个Employee对象,该对象具有

  • 我需要通过在Hibernate中执行本机查询获得结果集。虽然我使用的是EntityManager,但查询和结果集可能不是实体。 当我尝试下面的代码时,我得到了一个结果。因为我要求的是一个有值的结果。(Hibernate JPA) 结果是:爱丽丝 当我试图从相同的代码中获得多个select out(查询为select name)时,指定来自fresher_test(其中id=1) 这有一种方法,我可

  • 哪种方法是以CSV格式导出JPA查询结果的最佳方法?我试过opencsv,但它需要一个java.sql.ResultSet对象,我不明白如何创建它。我尝试了以下代码 但我收到异常java.lang.ClassCastException:java.util.Vector不能强制转换为java.sql.ResultSet

  • 查询方法能对以JAVA 8的Stream为返回的结果进行逐步处理。而不是简单地包装查询结果在被用来执行流的流数据存储特定的方法。 示例11。以JAVA 8的Stream来进行查询的流处理结果 @Query("select u from User u") Stream<User> findAllByCustomQueryAndStream(); Stream<User>

  • 问题内容: 在Spring Data JPA文档中,它说明了有关流的信息: Stream可能包装了基础数据存储的特​​定资源,因此必须在使用后关闭。您可以使用close()方法或使用Java 7 try-with- resources块来手动关闭Stream。 参见:http : //docs.spring.io/spring- data/jpa/docs/1.10.1.RELEASE/refer