使用Hibernate 5.2将结果查询为流

kma*_*las 17 java hibernate jpa stream scrollableresults

从Hibernate 5.2开始,我们可以使用该stream()方法而不是scroll()我们想要获取大量数据.

但是,当使用时scroll(),ScrollableResults我们能够通过在处理它之后从持久化上下文中驱逐对象和/或不时地清除整个会话来钩住检索过程并释放内存.

我的问题:

  1. 现在,如果我们使用该stream()方法,幕后会发生什么?
  2. 是否可以从持久化上下文中逐出对象?
  3. 会议是否定期结算?
  4. 如何实现最佳内存消耗?
  5. 有可能使用例如StatelessSession吗?
  6. 此外,如果我们hibernate.jdbc.fetch_size在JPA属性中设置了一些数字(例如1000),那么它如何与可滚动结果很好地结合?

wil*_*ing 9

以下适用于我:

DataSourceConfig.java

@Bean
public LocalSessionFactoryBean sessionFactory() {
    // Link your data source to your session factory
    ...
}

@Bean("hibernateTxManager")
public HibernateTransactionManager hibernateTxManager(@Qualifier("sessionFactory") SessionFactory sessionFactory) {
    // Link your session factory to your transaction manager
    ...
}
Run Code Online (Sandbox Code Playgroud)

MyServiceImpl.java

@Service
@Transactional(propagation = Propagation.REQUIRES_NEW, transactionManager = "hibernateTxManager", readOnly = true)
public class MyServiceImpl implements MyService {

    @Autowired
    private MyRepo myRepo;
    ...
    Stream<MyEntity> stream = myRepo.getStream();
    // Do your streaming and CLOSE the steam afterwards
    ...
Run Code Online (Sandbox Code Playgroud)

MyRepoImpl.java

@Repository
@Transactional(propagation = Propagation.MANDATORY, transactionManager = "hibernateTxManager", readOnly = true)
public class MyRepoImpl implements MyRepo {

    @Autowired
    private SessionFactory sessionFactory;

    @Autowired
    private MyDataSource myDataSource;

    public Stream<MyEntity> getStream() {

        return sessionFactory.openStatelessSession(DataSourceUtils.getConnection(myDataSource))
            .createNativeQuery("my_query", MyEntity.class)
            .setReadOnly(true)
            .setFetchSize(1000)
            .stream();
    }
    ...
Run Code Online (Sandbox Code Playgroud)

请记住,当你流式传输时,实际上只需要在物体实现时谨慎记忆.这确实是操作中唯一容易受到内存问题影响的部分.在我的例子中,我一次将流分块为1000个对象,使用gson将它们序列化并立即将它们发送给JMS代理.垃圾收集器完成其余的工作.

值得注意的是,Spring的事务边界感知最终会关闭与dB的连接,而无需明确告知.

  • 我通过大量的性能测试和分析工具测量了内存占用量。我最终将 6500 万条记录(获取大小 1000,然后具体化为每个 JMS 消息 1000 条记录)直接流式传输到 Solace。如果您需要这方面的代码,我可以添加它。如果您不使用本机查询或无状态会话,我建议向 hibernate 添加提示以告诉它可缓存 = false。有什么原因不能进行无状态会话吗?我们的代码存在于 Spring 事务边界内,并且与我们执行的任何其他读取操作方式相同。 (2认同)

小智 5

Hibernate ORM用户指南说明了这一点

在内部,stream()的行为类似于Query#scroll,底层结果由ScrollableResults支持.

您可以检查源代码,org.hibernate.query.internal.AbstractProducedQuery以确保您有责任定期清除会话或从持久上下文中逐出对象.

我从评论中了解到,StatelessSession不适合你.我认为解决你案件的干净方法是实现你自己的stream()方法.它可能与原始方法非常相似,只需ScrollableResultsIterator用您自己的方法替换,即在迭代期间执行您需要的操作(逐出对象或清除会话).