Важное примечание: это было принято как 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, и поскольку API Streams делает этот подход очень удобным, я хотел бы решить эту проблему поддерживаемым способом.
Есть ли способ получить такое же поведение без моего взлома? Если нет, было бы хорошо спросить через Spring Jira?