66

重要な注意: これは、ターゲット修正バージョン 4.1.2のSpring の問題として受け入れられています。


私の目標は、Hibernate の から HTTP 応答を生成するときに、O(1) 空間の複雑さを達成することScrollableResultsです。MessageConverteraから返されたオブジェクトを処理するために a がディスパッチされる標準的なメカニズムを維持したいと考えています@Controller。私は以下を設定しました:

  1. MappingJackson2HttpMessageConverterJsonSerializerJava 8 を処理するa で強化されましたStream
  2. ;ScrollableResultSpliteratorにラップScrollableResultsする必要があるカスタム。Stream
  3. OpenSessionInViewInterceptor内で Hibernate セッションを開いたままにしておく必要がありMessageConverterます。
  4. に設定hibernate.connection.release_modeON_CLOSEます。
  5. JDBC 接続に必要な ResultSet 保持機能があることを確認してくださいcon.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT)

さらに、そのような保持機能をサポートするデータベースが必要です。PostgreSQL はそのようなデータベースであり、これで問題はありません。

私が遭遇した最後のつまずき点は、HibernateTransactionManagerトランザクションのコミットで使用されるポリシーです。基礎となるセッションが「休止状態で管理されている」場合を除き、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 を介してリクエストするのに適していますか?

4

1 に答える 1