17
17
package org .springframework .orm .hibernate4 ;
18
18
19
19
import java .sql .Connection ;
20
+ import java .sql .ResultSet ;
20
21
import javax .sql .DataSource ;
21
22
22
23
import org .hibernate .ConnectionReleaseMode ;
@@ -117,6 +118,8 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
117
118
118
119
private boolean prepareConnection = true ;
119
120
121
+ private boolean allowResultAccessAfterCompletion = false ;
122
+
120
123
private boolean hibernateManagedSession = false ;
121
124
122
125
private Object entityInterceptor ;
@@ -229,6 +232,21 @@ public void setPrepareConnection(boolean prepareConnection) {
229
232
this .prepareConnection = prepareConnection ;
230
233
}
231
234
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
+
232
250
/**
233
251
* Set whether to operate on a Hibernate-managed Session instead of a
234
252
* Spring-managed Session, that is, whether to obtain the Session through
@@ -432,6 +450,13 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
432
450
Connection con = ((SessionImplementor ) session ).connection ();
433
451
Integer previousIsolationLevel = DataSourceUtils .prepareConnectionForTransaction (con , definition );
434
452
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
+ }
435
460
}
436
461
else {
437
462
// Not allowed to change the transaction settings of the JDBC Connection.
@@ -625,11 +650,18 @@ protected void doCleanupAfterCompletion(Object transaction) {
625
650
// Else, we need to rely on the connection pool to perform proper cleanup.
626
651
try {
627
652
Connection con = ((SessionImplementor ) session ).connection ();
653
+ Integer previousHoldability = txObject .getPreviousHoldability ();
654
+ if (previousHoldability != null ) {
655
+ con .setHoldability (previousHoldability );
656
+ }
628
657
DataSourceUtils .resetConnectionAfterTransaction (con , txObject .getPreviousIsolationLevel ());
629
658
}
630
659
catch (HibernateException ex ) {
631
660
logger .debug ("Could not access JDBC Connection of Hibernate Session" , ex );
632
661
}
662
+ catch (Throwable ex ) {
663
+ logger .debug ("Could not reset JDBC Connection after transaction" , ex );
664
+ }
633
665
}
634
666
635
667
if (txObject .isNewSession ()) {
@@ -645,13 +677,26 @@ protected void doCleanupAfterCompletion(Object transaction) {
645
677
if (txObject .getSessionHolder ().getPreviousFlushMode () != null ) {
646
678
session .setFlushMode (txObject .getSessionHolder ().getPreviousFlushMode ());
647
679
}
648
- if (!this .hibernateManagedSession ) {
649
- session . disconnect ( );
680
+ if (!this .allowResultAccessAfterCompletion && ! this . hibernateManagedSession ) {
681
+ disconnectOnCompletion ( session );
650
682
}
651
683
}
652
684
txObject .getSessionHolder ().clear ();
653
685
}
654
686
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
+
655
700
/**
656
701
* Return whether the given Hibernate Session will always hold the same
657
702
* JDBC Connection. This is used to check whether the transaction manager
@@ -698,6 +743,8 @@ private class HibernateTransactionObject extends JdbcTransactionObjectSupport {
698
743
699
744
private boolean newSession ;
700
745
746
+ private Integer previousHoldability ;
747
+
701
748
public void setSession (Session session ) {
702
749
this .sessionHolder = new SessionHolder (session );
703
750
this .newSessionHolder = true ;
@@ -728,6 +775,14 @@ public boolean isNewSession() {
728
775
return this .newSession ;
729
776
}
730
777
778
+ public void setPreviousHoldability (Integer previousHoldability ) {
779
+ this .previousHoldability = previousHoldability ;
780
+ }
781
+
782
+ public Integer getPreviousHoldability () {
783
+ return this .previousHoldability ;
784
+ }
785
+
731
786
public boolean hasSpringManagedTransaction () {
732
787
return (this .sessionHolder != null && this .sessionHolder .getTransaction () != null );
733
788
}
0 commit comments