在Spring MVC中使用ScrollableResults支持的Stream作为返回类型时遇到问题

Mar*_*nik 66 java hibernate open-session-in-view spring-orm java-8


重要说明:这已被接受为具有4.1.2的目标修订版本的Spring问题.


我的目标是在从Hibernate生成HTTP响应时实现O(1)空间复杂度ScrollableResults.我想保留MessageConverter调度a的标准机制来处理从a返回的对象@Controller.我已经设置了以下内容:

  1. MappingJackson2HttpMessageConverter丰富了JsonSerializer处理Java 8的a Stream;
  2. 定制ScrollableResultSpliterator包装需要ScrollableResultsStream;
  3. OpenSessionInViewInterceptor需要保持Hibernate会话在MessageConverter;
  4. 设置hibernate.connection.release_modeON_CLOSE;
  5. 确保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; }
}
Run Code Online (Sandbox Code Playgroud)

因为我认为我的方法是生成ResultSet支持的响应的"正确方法",并且由于Streams API使这种方法非常方便,所以我想以支持的方式解决这个问题.

有没有办法在没有我的黑客的情况下获得相同的行为?如果没有,通过Spring的Jira请求这是一件好事吗?

小智 1

打扫干净。正如马尔科·托波尔尼克在这里所说的

\n\n
\n

是的,我错过了这部分,即仅在遇到预先存在的会话时才应用可保持性设置。这意味着我的“想法”如何完成它已经是它的完成方式。这也意味着我对失败的评论不适用:您要么想要可保持性并跳过会话断开\xe2\x80\x94,要么您都不需要。因此,如果您无法获得可保持性,则没有理由不在提交时断开会话,因此在这种情况下没有理由激活“allowResultSetAccessAfterCompletion”。

\n
\n

  • 我不确定你的观点,但 Spring *确实*做出了更改来支持我的要求。您引用的评论是关于如何实现“allowResultSetAccessAfterCompletion”的非常技术细节,并且对用户几乎没有任何有用的贡献。这是我第一次审查实施时的误解造成的。 (2认同)