Результаты запроса в виде потока с Hibernate 5.2

Начиная с Hibernate 5.2, мы можем использовать stream() метод вместо scroll() если мы хотим получить большой объем данных.

Однако при использовании scroll() с ScrollableResults мы можем подключиться к процессу поиска и освободить память, либо удалив объект из постоянного контекста после его обработки и / или очистив весь сеанс время от времени.

Мои вопросы:

  1. Теперь, если мы используем stream() метод, что происходит за кадром?
  2. Возможно ли выселить объект из постоянного контекста?
  3. Периодически очищается сеанс?
  4. Как достигается оптимальное потребление памяти?
  5. Можно ли использовать, например, StatelessSession?
  6. Кроме того, если мы установили hibernate.jdbc.fetch_size к какому-то числу (например, 1000) в свойствах JPA, тогда как это хорошо сочетается с прокручиваемыми результатами?

2 ответа

Следующие работы для меня:

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

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 закрывает соединение с дБ в конце без необходимости явного уведомления.

Руководство пользователя Hibernate ORM гласит, что

Внутренне stream() ведет себя как прокрутка Query#, а базовый результат поддерживается ScrollableResults.

Вы можете проверить исходный код org.hibernate.query.internal.AbstractProducedQuery убедиться, что вы обязаны периодически очищать сеанс или исключать объект из постоянного контекста.

Как я понимаю из комментариев, StatelessSession это не вариант для вас. Я думаю, что чистый способ решить ваше дело - реализовать свой собственный stream() метод. Это может быть очень похоже на оригинальный метод, просто заменить ScrollableResultsIterator с вашим собственным, который будет делать то, что вам нужно (удалить объект или очистить сеанс) во время итерации.

Другие вопросы по тегам