Java 使用Hibernate 5.2将查询结果作为流

Java 使用Hibernate 5.2将查询结果作为流,java,hibernate,jpa,stream,scrollableresults,Java,Hibernate,Jpa,Stream,Scrollableresults,自Hibernate 5.2以来,如果我们想要获取大量数据,我们可以使用stream()方法而不是scroll() 但是,当将scroll()与ScrollableResults一起使用时,我们可以通过在处理对象后将其从持久性上下文中逐出和/或不时清除整个会话,来连接检索过程并释放内存 我的问题是: 现在,如果我们使用stream()方法,那么幕后会发生什么 是否可以从持久性上下文中逐出对象 会话是否定期清除 如何实现最佳内存消耗 是否可以使用无状态会话 另外,如果我们在JPA属性中将hiber

自Hibernate 5.2以来,如果我们想要获取大量数据,我们可以使用
stream()
方法而不是
scroll()

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

我的问题是:

  • 现在,如果我们使用
    stream()
    方法,那么幕后会发生什么
  • 是否可以从持久性上下文中逐出对象
  • 会话是否定期清除
  • 如何实现最佳内存消耗
  • 是否可以使用无状态会话
  • 另外,如果我们在JPA属性中将
    hibernate.jdbc.fetch_size
    设置为某个数字(例如1000),那么如何将其与可滚动结果很好地结合起来呢

  • 以下是我的作品:

    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
        ...
    }
    
    @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
        ...
    
    @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();
        }
        ...
    
    MyServiceImpl.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
        ...
    }
    
    @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
        ...
    
    @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();
        }
        ...
    
    @服务
    @事务性(propagation=propagation.REQUIRES_NEW,transactionManager=“hibernateTxManager”,readOnly=true)
    公共类MyServiceImpl实现了MyService{
    @自动连线
    私人MyRepo MyRepo;
    ...
    Stream=myRepo.getStream();
    //做你的流和关闭蒸汽后
    ...
    
    MyRepoImpl.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
        ...
    }
    
    @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
        ...
    
    @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();
        }
        ...
    
    @存储库
    @事务性(propagation=propagation.MANDATORY,transactionManager=“hibernateTxManager”,readOnly=true)
    public类myrepimpl实现MyRepo{
    @自动连线
    私人会话工厂会话工厂;
    @自动连线
    私有MyDataSource MyDataSource;
    公共流getStream(){
    返回sessionFactory.openStatelessSession(DataSourceUtils.getConnection(myDataSource))
    .createNativeQuery(“我的查询”,MyEntity.class)
    .setReadOnly(真)
    .setFetchSize(1000)
    .stream();
    }
    ...
    
    请记住,流式处理时,只需在对象具体化时小心内存。这确实是操作中唯一容易出现内存问题的部分。在我的例子中,我一次将1000个对象分块处理流,用gson序列化它们,并立即将它们发送给JMS代理。垃圾收集器会这样做其余的都是

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

    Hibernate ORM用户指南

    在内部,stream()的行为类似于查询#滚动,底层结果由ScrollableResults支持

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


    我从评论中了解到,
    无状态会话
    不适合您。我认为解决您的问题的干净方法是实现您自己的
    stream()
    方法。它可能与原始方法非常相似,只需将
    ScrollableResultsIterator
    替换为您自己的方法即可(逐出对象或清除会话)在迭代过程中。

    您是否愿意与我们分享您是如何测量内存占用的?我们还注意到,您使用了本机查询,而在我们的情况下,本机查询不是一个选项。说我们也不能使用无状态会话。我通过大量的性能测试和分析工具测量了内存占用。最后,我流式处理了6500万次n条记录(获取大小为1000条,然后具体化为每个JMS消息1000条记录)直接进入安慰。如果您需要这方面的代码,我可以添加它。如果您不使用本机查询或无状态会话,我建议您向hibernate添加一个提示,告诉它cacheable=false。有什么原因不能使用无状态会话吗?我们的代码存在于Spring事务边界内,并且与其他会话的操作方式相同我们将执行的其他读取。在我们的应用程序中,实体管理器已禁用二级缓存。我们不能使用无状态会话的另一个原因是我们有动态条件查询。只要在事务后连接关闭,并且您使用的是最小缓存,您就应该可以。@wild\u nothing:您能通知我们吗您使用的是哪一个数据库?因为使用
    无状态会话
    会产生不同,而且用户需要定期清除会话等。我不太确定
    可滚动结果
    流()的附加值是多少
    在hibernate中。我还阅读了以下文章:,这是促使我调查此问题的文章。与此主题相关的有趣文章:相关hibernate JIRA问题: