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

时间:2014-10-12 10:23:07

标签: java hibernate java-8 spring-orm open-session-in-view


重要说明:此版本已被接受为Spring issue,其目标修订版本为4.1.2。


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

  1. MappingJackson2HttpMessageConverter使用JsonSerializer来处理Java 8 Stream;
  2. 自定义ScrollableResultSpliterator需要将ScrollableResults包裹到Stream;
  3. OpenSessionInViewInterceptor需要在MessageConverter;
  4. 中保持Hibernate会话的开放
  5. hibernate.connection.release_mode设为ON_CLOSE;
  6. 确保JDBC连接具有必要的ResultSet可保持性:con.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT)
  7. 此外,我需要一个支持这种可保持性的数据库。 PostgreSQL就是这样一个数据库,我对此没有任何麻烦。

    我遇到的最后一个绊脚石是HibernateTransactionManager在事务提交中使用的策略:除非底层会话是“Hibernate-managed”,否则它将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的Jira请求这是一件好事吗?

1 个答案:

答案 0 :(得分:1)

清理。正如Marko Topolnik所说here

  

是的,我错过了这一部分,只有在遇到预先存在的会话时才会应用可保持性设置。这意味着我的想法&#34;它是如何完成的已经是它的完成方式。这也意味着我对失败的评论并不适用:你要么想要保持力,要么跳过会话断开连接 - 或者你也不需要。因此,如果您无法获得可保持性,则没有理由不在提交时断开会话,因此没有理由激活&#34; allowResultSetAccessAfterCompletion&#34;在那种情况下。