1717package org .springframework .orm .hibernate4 ;
1818
1919import java .sql .Connection ;
20+ import java .sql .ResultSet ;
2021import javax .sql .DataSource ;
2122
2223import org .hibernate .ConnectionReleaseMode ;
@@ -117,6 +118,8 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
117118
118119 private boolean prepareConnection = true ;
119120
121+ private boolean allowResultAccessAfterCompletion = false ;
122+
120123 private boolean hibernateManagedSession = false ;
121124
122125 private Object entityInterceptor ;
@@ -229,6 +232,21 @@ public void setPrepareConnection(boolean prepareConnection) {
229232 this .prepareConnection = prepareConnection ;
230233 }
231234
235+ /**
236+ * Set whether to allow result access after completion, typically via Hibernate's
237+ * ScrollableResults mechanism.
238+ * <p>Default is "false". Turning this flag on enforces over-commit holdability on the
239+ * underlying JDBC Connection (if {@link #prepareConnection "prepareConnection"} is on)
240+ * and skips the disconnect-on-completion step.
241+ * @since 4.1.2
242+ * @see java.sql.Connection#setHoldability
243+ * @see ResultSet#HOLD_CURSORS_OVER_COMMIT
244+ * @see #disconnectOnCompletion(Session)
245+ */
246+ public void setAllowResultAccessAfterCompletion (boolean allowResultAccessAfterCompletion ) {
247+ this .allowResultAccessAfterCompletion = allowResultAccessAfterCompletion ;
248+ }
249+
232250 /**
233251 * Set whether to operate on a Hibernate-managed Session instead of a
234252 * Spring-managed Session, that is, whether to obtain the Session through
@@ -432,6 +450,13 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
432450 Connection con = ((SessionImplementor ) session ).connection ();
433451 Integer previousIsolationLevel = DataSourceUtils .prepareConnectionForTransaction (con , definition );
434452 txObject .setPreviousIsolationLevel (previousIsolationLevel );
453+ if (this .allowResultAccessAfterCompletion && !txObject .isNewSession ()) {
454+ int currentHoldability = con .getHoldability ();
455+ if (currentHoldability != ResultSet .HOLD_CURSORS_OVER_COMMIT ) {
456+ txObject .setPreviousHoldability (currentHoldability );
457+ con .setHoldability (ResultSet .HOLD_CURSORS_OVER_COMMIT );
458+ }
459+ }
435460 }
436461 else {
437462 // Not allowed to change the transaction settings of the JDBC Connection.
@@ -625,11 +650,18 @@ protected void doCleanupAfterCompletion(Object transaction) {
625650 // Else, we need to rely on the connection pool to perform proper cleanup.
626651 try {
627652 Connection con = ((SessionImplementor ) session ).connection ();
653+ Integer previousHoldability = txObject .getPreviousHoldability ();
654+ if (previousHoldability != null ) {
655+ con .setHoldability (previousHoldability );
656+ }
628657 DataSourceUtils .resetConnectionAfterTransaction (con , txObject .getPreviousIsolationLevel ());
629658 }
630659 catch (HibernateException ex ) {
631660 logger .debug ("Could not access JDBC Connection of Hibernate Session" , ex );
632661 }
662+ catch (Throwable ex ) {
663+ logger .debug ("Could not reset JDBC Connection after transaction" , ex );
664+ }
633665 }
634666
635667 if (txObject .isNewSession ()) {
@@ -645,13 +677,26 @@ protected void doCleanupAfterCompletion(Object transaction) {
645677 if (txObject .getSessionHolder ().getPreviousFlushMode () != null ) {
646678 session .setFlushMode (txObject .getSessionHolder ().getPreviousFlushMode ());
647679 }
648- if (!this .hibernateManagedSession ) {
649- session . disconnect ( );
680+ if (!this .allowResultAccessAfterCompletion && ! this . hibernateManagedSession ) {
681+ disconnectOnCompletion ( session );
650682 }
651683 }
652684 txObject .getSessionHolder ().clear ();
653685 }
654686
687+ /**
688+ * Disconnect a pre-existing Hibernate Session on transaction completion,
689+ * returning its database connection but preserving its entity state.
690+ * <p>The default implementation simply calls {@link Session#disconnect()}.
691+ * Subclasses may override this with a no-op or with fine-tuned disconnection logic.
692+ * @param session the Hibernate Session to disconnect
693+ * @since 4.1.2
694+ * @see org.hibernate.Session#disconnect()
695+ */
696+ protected void disconnectOnCompletion (Session session ) {
697+ session .disconnect ();
698+ }
699+
655700 /**
656701 * Return whether the given Hibernate Session will always hold the same
657702 * JDBC Connection. This is used to check whether the transaction manager
@@ -698,6 +743,8 @@ private class HibernateTransactionObject extends JdbcTransactionObjectSupport {
698743
699744 private boolean newSession ;
700745
746+ private Integer previousHoldability ;
747+
701748 public void setSession (Session session ) {
702749 this .sessionHolder = new SessionHolder (session );
703750 this .newSessionHolder = true ;
@@ -728,6 +775,14 @@ public boolean isNewSession() {
728775 return this .newSession ;
729776 }
730777
778+ public void setPreviousHoldability (Integer previousHoldability ) {
779+ this .previousHoldability = previousHoldability ;
780+ }
781+
782+ public Integer getPreviousHoldability () {
783+ return this .previousHoldability ;
784+ }
785+
731786 public boolean hasSpringManagedTransaction () {
732787 return (this .sessionHolder != null && this .sessionHolder .getTransaction () != null );
733788 }
0 commit comments