1
1
/*
2
- * Copyright 2002-2013 the original author or authors.
2
+ * Copyright 2002-2014 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
55
55
import org .springframework .dao .InvalidDataAccessResourceUsageException ;
56
56
import org .springframework .dao .PessimisticLockingFailureException ;
57
57
import org .springframework .jdbc .datasource .ConnectionHandle ;
58
+ import org .springframework .jdbc .datasource .DataSourceUtils ;
58
59
import org .springframework .jdbc .support .JdbcUtils ;
59
60
import org .springframework .orm .ObjectOptimisticLockingFailureException ;
60
61
import org .springframework .orm .ObjectRetrievalFailureException ;
61
62
import org .springframework .orm .jpa .DefaultJpaDialect ;
62
63
import org .springframework .orm .jpa .EntityManagerFactoryUtils ;
63
64
import org .springframework .orm .jpa .JpaSystemException ;
65
+ import org .springframework .transaction .InvalidIsolationLevelException ;
64
66
import org .springframework .transaction .TransactionDefinition ;
65
67
import org .springframework .transaction .TransactionException ;
66
68
import org .springframework .util .ClassUtils ;
70
72
* {@link org.springframework.orm.jpa.JpaDialect} implementation for
71
73
* Hibernate EntityManager. Developed against Hibernate 3.6 and 4.2/4.3.
72
74
*
73
- * @author Costin Leau
74
75
* @author Juergen Hoeller
76
+ * @author Costin Leau
75
77
* @since 2.0
76
78
*/
77
79
@ SuppressWarnings ({"serial" , "deprecation" })
@@ -100,22 +102,73 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
100
102
}
101
103
102
104
105
+ private boolean prepareConnection = (HibernateConnectionHandle .sessionConnectionMethod == null );
106
+
107
+
108
+ /**
109
+ * Set whether to prepare the underlying JDBC Connection of a transactional
110
+ * Hibernate Session, that is, whether to apply a transaction-specific
111
+ * isolation level and/or the transaction's read-only flag to the underlying
112
+ * JDBC Connection.
113
+ * <p>Default is "true" on Hibernate EntityManager 4.x (with its 'on-close'
114
+ * connection release mode, and "false" on Hibernate EntityManager 3.6 (due to
115
+ * the 'after-transaction' release mode there). <b>Note that Hibernate 4.2+ is
116
+ * strongly recommended in order to make isolation levels work efficiently.</b>
117
+ * <p>If you turn this flag off, JPA transaction management will not support
118
+ * per-transaction isolation levels anymore. It will not call
119
+ * {@code Connection.setReadOnly(true)} for read-only transactions anymore either.
120
+ * If this flag is turned off, no cleanup of a JDBC Connection is required after
121
+ * a transaction, since no Connection settings will get modified.
122
+ * @see java.sql.Connection#setTransactionIsolation
123
+ * @see java.sql.Connection#setReadOnly
124
+ */
125
+ public void setPrepareConnection (boolean prepareConnection ) {
126
+ this .prepareConnection = prepareConnection ;
127
+ }
128
+
129
+
103
130
@ Override
104
131
public Object beginTransaction (EntityManager entityManager , TransactionDefinition definition )
105
132
throws PersistenceException , SQLException , TransactionException {
106
133
134
+ Session session = getSession (entityManager );
135
+
107
136
if (definition .getTimeout () != TransactionDefinition .TIMEOUT_DEFAULT ) {
108
- getSession ( entityManager ) .getTransaction ().setTimeout (definition .getTimeout ());
137
+ session .getTransaction ().setTimeout (definition .getTimeout ());
109
138
}
110
- super .beginTransaction (entityManager , definition );
111
- return prepareTransaction (entityManager , definition .isReadOnly (), definition .getName ());
139
+
140
+ Integer previousIsolationLevel = null ;
141
+ boolean isolationLevelNeeded = (definition .getIsolationLevel () != TransactionDefinition .ISOLATION_DEFAULT );
142
+ if (isolationLevelNeeded || definition .isReadOnly ()) {
143
+ if (this .prepareConnection ) {
144
+ Connection con = HibernateConnectionHandle .doGetConnection (session );
145
+ previousIsolationLevel = DataSourceUtils .prepareConnectionForTransaction (con , definition );
146
+ }
147
+ else if (isolationLevelNeeded ) {
148
+ throw new InvalidIsolationLevelException (getClass ().getSimpleName () +
149
+ " does not support custom isolation levels since the 'prepareConnection' flag is off. " +
150
+ "This is the case on Hibernate 3.6 by default; either switch that flag at your own risk " +
151
+ "or upgrade to Hibernate 4.x, with 4.2+ recommended." );
152
+ }
153
+ }
154
+
155
+ // Standard JPA transaction begin call for full JPA context setup...
156
+ entityManager .getTransaction ().begin ();
157
+
158
+ // Adapt flush mode and store previous isolation level, if any.
159
+ return doPrepareTransaction (session , definition .isReadOnly (), previousIsolationLevel );
112
160
}
113
161
114
162
@ Override
115
163
public Object prepareTransaction (EntityManager entityManager , boolean readOnly , String name )
116
164
throws PersistenceException {
117
165
118
- Session session = getSession (entityManager );
166
+ return doPrepareTransaction (getSession (entityManager ), readOnly , null );
167
+ }
168
+
169
+ protected Object doPrepareTransaction (Session session , boolean readOnly , Integer previousIsolationLevel )
170
+ throws PersistenceException {
171
+
119
172
FlushMode flushMode = session .getFlushMode ();
120
173
FlushMode previousFlushMode = null ;
121
174
if (readOnly ) {
@@ -130,12 +183,14 @@ public Object prepareTransaction(EntityManager entityManager, boolean readOnly,
130
183
previousFlushMode = flushMode ;
131
184
}
132
185
}
133
- return new SessionTransactionData (session , previousFlushMode );
186
+
187
+ boolean resetConnection = (previousIsolationLevel != null || readOnly );
188
+ return new SessionTransactionData (session , previousFlushMode , resetConnection , previousIsolationLevel );
134
189
}
135
190
136
191
@ Override
137
192
public void cleanupTransaction (Object transactionData ) {
138
- ((SessionTransactionData ) transactionData ).resetFlushMode ();
193
+ ((SessionTransactionData ) transactionData ).resetSessionState ();
139
194
}
140
195
141
196
@ Override
@@ -255,15 +310,26 @@ private static class SessionTransactionData {
255
310
256
311
private final FlushMode previousFlushMode ;
257
312
258
- public SessionTransactionData (Session session , FlushMode previousFlushMode ) {
313
+ private final boolean connectionReset ;
314
+
315
+ private final Integer previousIsolationLevel ;
316
+
317
+ public SessionTransactionData (
318
+ Session session , FlushMode previousFlushMode , boolean resetConnection , Integer previousIsolationLevel ) {
259
319
this .session = session ;
260
320
this .previousFlushMode = previousFlushMode ;
321
+ this .connectionReset = resetConnection ;
322
+ this .previousIsolationLevel = previousIsolationLevel ;
261
323
}
262
324
263
- public void resetFlushMode () {
325
+ public void resetSessionState () {
264
326
if (this .previousFlushMode != null ) {
265
327
this .session .setFlushMode (this .previousFlushMode );
266
328
}
329
+ if (this .connectionReset && this .session .isConnected ()) {
330
+ Connection con = HibernateConnectionHandle .doGetConnection (this .session );
331
+ DataSourceUtils .resetConnectionAfterTransaction (con , this .previousIsolationLevel );
332
+ }
267
333
}
268
334
}
269
335
@@ -284,16 +350,7 @@ public HibernateConnectionHandle(Session session) {
284
350
285
351
@ Override
286
352
public Connection getConnection () {
287
- try {
288
- if (connectionMethodToUse == null ) {
289
- // Reflective lookup trying to find SessionImpl's connection() on Hibernate 4.x
290
- connectionMethodToUse = this .session .getClass ().getMethod ("connection" );
291
- }
292
- return (Connection ) ReflectionUtils .invokeMethod (connectionMethodToUse , this .session );
293
- }
294
- catch (NoSuchMethodException ex ) {
295
- throw new IllegalStateException ("Cannot find connection() method on Hibernate Session" , ex );
296
- }
353
+ return doGetConnection (this .session );
297
354
}
298
355
299
356
@ Override
@@ -307,6 +364,19 @@ public void releaseConnection(Connection con) {
307
364
JdbcUtils .closeConnection (con );
308
365
}
309
366
}
367
+
368
+ public static Connection doGetConnection (Session session ) {
369
+ try {
370
+ if (connectionMethodToUse == null ) {
371
+ // Reflective lookup trying to find SessionImpl's connection() on Hibernate 4.x
372
+ connectionMethodToUse = session .getClass ().getMethod ("connection" );
373
+ }
374
+ return (Connection ) ReflectionUtils .invokeMethod (connectionMethodToUse , session );
375
+ }
376
+ catch (NoSuchMethodException ex ) {
377
+ throw new IllegalStateException ("Cannot find connection() method on Hibernate Session" , ex );
378
+ }
379
+ }
310
380
}
311
381
312
382
}
0 commit comments