18

从 Hibernate 5.2 开始,如果我们想要获取大量数据,我们可以使用该stream()方法代替。scroll()

但是,当使用scroll()with时,ScrollableResults我们可以挂钩到检索过程并通过在处理对象后将对象从持久上下文中逐出和/或不时清除整个会话来释放内存。

我的问题:

  1. 现在,如果我们使用该stream()方法,幕后会发生什么?
  2. 是否可以从持久上下文中驱逐对象?
  3. 会话是否定期清除?
  4. 如何实现最佳内存消耗?
  5. 是否可以使用例如 StatelessSession?
  6. 此外,如果我们hibernate.jdbc.fetch_size在 JPA 属性中设置了某个数字(例如 1000),那么这如何与可滚动结果很好地结合起来呢?
4

2 回答 2

8

以下对我有用:

数据源配置.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
    ...
}

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
    ...

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();
    }
    ...

请记住,当您进行流式传输时,您实际上只需要在对象物化时注意内存。这确实是该操作中唯一容易受到内存问题影响的部分。就我而言,我一次将流分块为 1000 个对象,使用 gson 将它们序列化并立即将它们发送到 JMS 代理。垃圾收集器完成其余的工作。

值得注意的是,Spring 的事务边界感知在最后关闭了与 dB 的连接,无需明确告知。

于 2017-05-19T17:50:49.430 回答
5

Hibernate ORM 用户指南指出

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

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

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

于 2017-05-29T07:43:01.610 回答