Hibernate .getResultStream() 没有流式传输?
Hibernate .getResultStream() not streaming?
我正在尝试通过 .getResultStream()
使用 Hibernate 从 MySQL 流式传输大量数据,但出现了 OutOfMemoryErrors。
一段时间后在第一行加载时,我会看到这个异常:
at com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:1963) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:3308) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:463) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3032) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2280) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2673) ~[mysql-connector-java-5.1.37.jar:5.1.37]
这里让我感到惊讶的是 readAllResults
- 这似乎不是真正的流式传输。
我也试过用:
query.setHint("org.hibernate.fetchSize", 1000)
没有效果。
然后我升级到 MySQL Connector 8.0.19 和 Hibernate 5.4。11.Final 效果相同。
以下仅适用于没有 HHH-9340. Correct answer is the 的 Hibernate 版本。
这是正确的观察。方法 getResultStream
只是将结果列表视为 Stream
。根据API documentation:
By default this method delegates to getResultList().stream(), however
persistence provider may choose to override this method to provide
additional capabilities.
Hibernate选择了不覆盖这个方法,从QueryImpl和类可以看出它继承自
有趣的是,接受的答案是错误的。正如 Mikko 所提到的,JPA 规范将实现留给了供应商,而 Hibernate 实际实现了它,请参阅 QueryImpl 实现的 Query class。
现在,原因是 MySQL 的行为方式。如上所述here, MySQL by default load all result to memory. You have 2 options, check here -> https://vladmihalcea.com/whats-new-in-jpa-2-2-stream-the-result-of-a-query-execution/
基本上,要么将 Statement fetch size 设置为 Integer.MIN_VALUE 然后每条记录将一个接一个地获取,或者将 useCursorFetch connection 属性 设置为 true 然后你可以设置 Statement fetch大小为正整数值(或设置 defaultFetchSize 连接 属性 这样您就不需要为每个查询都定义)
我正在尝试通过 .getResultStream()
使用 Hibernate 从 MySQL 流式传输大量数据,但出现了 OutOfMemoryErrors。
一段时间后在第一行加载时,我会看到这个异常:
at com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:1963) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:3308) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:463) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3032) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2280) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2673) ~[mysql-connector-java-5.1.37.jar:5.1.37]
这里让我感到惊讶的是 readAllResults
- 这似乎不是真正的流式传输。
我也试过用:
query.setHint("org.hibernate.fetchSize", 1000)
没有效果。
然后我升级到 MySQL Connector 8.0.19 和 Hibernate 5.4。11.Final 效果相同。
以下仅适用于没有 HHH-9340. Correct answer is the
这是正确的观察。方法 getResultStream
只是将结果列表视为 Stream
。根据API documentation:
By default this method delegates to getResultList().stream(), however persistence provider may choose to override this method to provide additional capabilities.
Hibernate选择了不覆盖这个方法,从QueryImpl和类可以看出它继承自
有趣的是,接受的答案是错误的。正如 Mikko 所提到的,JPA 规范将实现留给了供应商,而 Hibernate 实际实现了它,请参阅 QueryImpl 实现的 Query class。
现在,原因是 MySQL 的行为方式。如上所述here, MySQL by default load all result to memory. You have 2 options, check here -> https://vladmihalcea.com/whats-new-in-jpa-2-2-stream-the-result-of-a-query-execution/
基本上,要么将 Statement fetch size 设置为 Integer.MIN_VALUE 然后每条记录将一个接一个地获取,或者将 useCursorFetch connection 属性 设置为 true 然后你可以设置 Statement fetch大小为正整数值(或设置 defaultFetchSize 连接 属性 这样您就不需要为每个查询都定义)