Массовое обновление Hibernate с использованием ScrollableResult закрывается при фиксации
Мы хотим изменить объемные данные, используя сеанс без сохранения состояния hibernate 5.2 и некоторый код, подобный этому:
Transaction transaction = session.getTransaction();
Query q = session.createQuery("from " + MyClass.class.getName());
ScrollableResults scroll = q.scroll(ScrollMode.SCROLL_INSENSITIVE);
int cnt = 0;
while (scroll.next()) {
MyClass obj = (MyClass) scoll.get(0);
// modify obj
session.update(obj);
if (++cnt % batchSize == 0)
transaction.commit();
}
transaction.commit();
Фиксация - это обычно каждые 1k объектов, так как полная таблица огромна. Использование hibernate 4 вышеупомянутый код работал в течение многих лет. Однако в hibernate 5.2.7 мы получаем следующую трассировку стека:
org.hibernate.exception.GenericJDBCException: could not advance using next()
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
at org.hibernate.internal.ScrollableResultsImpl.convert(ScrollableResultsImpl.java:69)
at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:104)
Caused by: java.sql.SQLException: You can't operate on a closed ResultSet!!!
at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:77)
at com.mchange.v2.c3p0.impl.NewProxyResultSet.next(NewProxyResultSet.java:691)
at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:99)
... 4 more
Caused by: java.lang.NullPointerException
at com.mchange.v2.c3p0.impl.NewProxyResultSet.next(NewProxyResultSet.java:685)
... 5 more
Причина в том, что ScrollableResults закрывается при фиксации. Это, кажется, предполагаемое поведение, так как:
http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html
"Если приложение не будет закрыто, Hibernate автоматически закроет базовые ресурсы (например, ResultSet и PreparedStatement), используемые внутри ScrollableResults, когда текущая транзакция будет завершена (фиксация или откат). Однако рекомендуется закрывать ScrollableResults явным образом. ".
Лучшее решение, кажется, открыть второй сеанс (и соединение), как это:
StatelessSession session = sessionFactory.openStatelessSession();
StatelessSession sessionCursor = sessionFactory.openStatelessSession();
Transaction transaction = session.getTransaction();
Query q = sessionCursor.createQuery("from " + MyClass.class.getName());
ScrollableResults scroll = q.scroll(ScrollMode.SCROLL_INSENSITIVE);
int cnt = 0;
while (scroll.next()) {
MyClass obj = (MyClass) scoll.get(0);
// modify obj
session.update(obj);
if (++cnt % batchSize == 0)
transaction.commit();
}
transaction.commit();
Это действительно лучшее решение? Интересно, как другие люди обновляют большой набор данных, используя прокручиваемый результат?