Результаты запроса в виде потока с Hibernate 5.2
Начиная с Hibernate 5.2, мы можем использовать stream()
метод вместо scroll()
если мы хотим получить большой объем данных.
Однако при использовании scroll()
с ScrollableResults
мы можем подключиться к процессу поиска и освободить память, либо удалив объект из постоянного контекста после его обработки и / или очистив весь сеанс время от времени.
Мои вопросы:
- Теперь, если мы используем
stream()
метод, что происходит за кадром? - Возможно ли выселить объект из постоянного контекста?
- Периодически очищается сеанс?
- Как достигается оптимальное потребление памяти?
- Можно ли использовать, например, StatelessSession?
- Кроме того, если мы установили
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
с вашим собственным, который будет делать то, что вам нужно (удалить объект или очистить сеанс) во время итерации.