在Spring MVC中使用ScrollableResults-backed Stream作为返回types时遇到困难
重要提示:这已被作为4.1.2的目标修复版本的Spring问题接受。
我的目标是在从Hibernate的ScrollableResults
生成HTTP响应时实现O(1)空间复杂性。 我想保留一个MessageConverter
分派的标准机制来处理从@Controller
返回的对象。 我已经设置了以下内容:
-
MappingJackson2HttpMessageConverter
了一个处理Java 8Stream
的JsonSerializer
; -
ScrollableResultSpliterator
需要将ScrollableResults
封装到Stream
; -
OpenSessionInViewInterceptor
需要保持Hibernate会话在MessageConverter
打开; - 设置
hibernate.connection.release_mode
为ON_CLOSE
; - 确保JDBC连接具有必需的ResultSet可保存性:
con.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT)
。
另外,我需要一个支持这种可保存性的数据库。 PostgreSQL是这样一个数据库,我没有这个问题。
我遇到的最后一个绊脚石是HibernateTransactionManager
对事务提交使用的策略:除非底层会话是“Hibernate-managed”,否则它将disconnect()
,closures我的游标以及所有其他事物。 这种政策在一些特殊情况下是有用的,特别是“对话范围内的会议”,这远远不符合我的要求。
我设法解决了这个问题:我不得不用一个方法覆盖违规的方法,除了删除的disconnect()
调用之外,它实际上是一个原始的复制粘贴,但是它必须通过reflection来访问private 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的Jira请求会是一件好事吗?
打扫干净。 正如Marko Topolnik在这里所说的那样
是的,我错过了这个部分,只有在遇到预先存在的会话时才应用可保存性设置。 这意味着我的“想法”是如何完成的。 这也意味着我对失败的评论不适用:你要么保持可移植性,要么跳过会话断开连接 – 否则你也不需要。 所以如果你不能保持可用性,没有理由不要在提交时断开会话,所以在这种情况下没有理由激活“allowResultSetAccessAfterCompletion”。