Проблема с использованием потока, поддерживаемого ScrollableResults, в качестве возвращаемого типа в Spring MVC
Важное примечание: это было принято как проблема Spring с целевой версией исправления 4.1.2.
Моя цель - достичь сложности пространства O(1) при генерации HTTP-ответа из Hibernate ScrollableResults
, Я хочу сохранить стандартный механизм, где MessageConverter
отправляется для обработки объекта, возвращенного из @Controller
, Я настроил следующее:
MappingJackson2HttpMessageConverter
обогащенJsonSerializer
который обрабатывает Java 8Stream
;- обычай
ScrollableResultSpliterator
нужно обернутьScrollableResults
вStream
; OpenSessionInViewInterceptor
необходимо держать сеанс Hibernate открытым вMessageConverter
;- задавать
hibernate.connection.release_mode
вON_CLOSE
; - убедитесь, что соединение JDBC имеет необходимую устойчивость ResultSet:
con.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT)
,
Кроме того, мне нужна база данных, которая поддерживает такую устойчивость. PostgreSQL - это такая база данных, и у меня нет проблем с этим.
Последний камень преткновения, с которым я столкнулся, - это политика, используемая HibernateTransactionManager
при фиксации транзакции: если основной сеанс не "Hibernate-управляемый", он будет disconnect()
это, закрывая мой курсор вместе со всем остальным. Такая политика полезна в некоторых особых сценариях, в частности в "сеансах с ограниченным объемом разговоров", которые далеки от моих требований.
Мне удалось обойти это с плохим взломом: мне пришлось переопределить оскорбительный метод с помощью метода, который фактически является копией-вставкой оригинала, за исключением удаленного disconnect()
вызов, но он должен прибегнуть к отражению для доступа к частному API.
public class NoDisconnectHibernateTransactionManager extends HibernateTransactionManager
{
private static final Logger logger = LoggerFactory.getLogger(NoDisconnectHibernateTransactionManager.class);
public NoDisconnectHibernateTransactionManager(SessionFactory sf) { super(sf); }
@Override
protected void doCleanupAfterCompletion(Object transaction) {
final JdbcTransactionObjectSupport txObject = (JdbcTransactionObjectSupport) transaction;
final Class<?> c = txObject.getClass();
try {
// Remove the session holder from the thread.
if ((Boolean)jailBreak(c.getMethod("isNewSessionHolder")).invoke(txObject))
TransactionSynchronizationManager.unbindResource(getSessionFactory());
// Remove the JDBC connection holder from the thread, if exposed.
if (getDataSource() != null)
TransactionSynchronizationManager.unbindResource(getDataSource());
final SessionHolder sessionHolder = (SessionHolder)jailBreak(c.getMethod("getSessionHolder")).invoke(txObject);
final Session session = sessionHolder.getSession();
if ((Boolean)jailBreak(HibernateTransactionManager.class.getDeclaredField("prepareConnection")).get(this)
&& session.isConnected() && isSameConnectionForEntireSession(session))
{
// We're running with connection release mode "on_close": We're able to reset
// the isolation level and/or read-only flag of the JDBC Connection here.
// Else, we need to rely on the connection pool to perform proper cleanup.
try {
final Connection con = ((SessionImplementor) session).connection();
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (HibernateException ex) {
logger.debug("Could not access JDBC Connection of Hibernate Session", ex);
}
}
if ((Boolean)jailBreak(c.getMethod("isNewSession")).invoke(txObject)) {
logger.debug("Closing Hibernate Session [{}] after transaction", session);
SessionFactoryUtils.closeSession(session);
}
else {
logger.debug("Not closing pre-bound Hibernate Session [{}] after transaction", session);
if (sessionHolder.getPreviousFlushMode() != null)
session.setFlushMode(sessionHolder.getPreviousFlushMode());
}
sessionHolder.clear();
}
catch (ReflectiveOperationException e) { throw new RuntimeException(e); }
}
static <T extends AccessibleObject> T jailBreak(T o) { o.setAccessible(true); return o; }
}
Поскольку я рассматриваю свой подход как "правильный путь" для генерации ответов, поддерживаемых ResultSet, и поскольку Streams API делает этот подход очень удобным, я хотел бы решить эту проблему поддерживаемым способом.
Есть ли способ получить такое же поведение без моего взлома? Если нет, то будет ли это хорошо запрашивать через Spring's Jira?
1 ответ
Убираться. Как сказал здесь Марко Топольник
Да, я пропустил эту часть, что настройка удерживаемости применяется только при встрече с уже существующим сеансом. Это означает, что моя "идея" о том, как это можно сделать, уже сделана. Это также означает, что мой комментарий о сбоях неприменим: вы либо хотите удержать и пропустить отключение сеанса, либо вам это тоже не нужно. Поэтому, если вы не можете получить возможность удержания, нет причин не отключать сеанс при фиксации, поэтому нет причины активировать "allowResultSetAccessAfterCompletion" в этом случае.