1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.spring;
17
18 import static org.springframework.util.Assert.notNull;
19
20 import org.apache.ibatis.exceptions.PersistenceException;
21 import org.apache.ibatis.logging.Log;
22 import org.apache.ibatis.logging.LogFactory;
23 import org.apache.ibatis.mapping.Environment;
24 import org.apache.ibatis.session.ExecutorType;
25 import org.apache.ibatis.session.SqlSession;
26 import org.apache.ibatis.session.SqlSessionFactory;
27 import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
28 import org.springframework.dao.DataAccessException;
29 import org.springframework.dao.TransientDataAccessResourceException;
30 import org.springframework.dao.support.PersistenceExceptionTranslator;
31 import org.springframework.jdbc.datasource.DataSourceUtils;
32 import org.springframework.transaction.support.TransactionSynchronizationAdapter;
33 import org.springframework.transaction.support.TransactionSynchronizationManager;
34
35
36
37
38
39
40
41
42
43
44 public final class SqlSessionUtils {
45
46 private static final Log LOGGER = LogFactory.getLog(SqlSessionUtils.class);
47
48 private static final String NO_EXECUTOR_TYPE_SPECIFIED = "No ExecutorType specified";
49 private static final String NO_SQL_SESSION_FACTORY_SPECIFIED = "No SqlSessionFactory specified";
50 private static final String NO_SQL_SESSION_SPECIFIED = "No SqlSession specified";
51
52
53
54
55 private SqlSessionUtils() {
56
57 }
58
59
60
61
62
63
64
65
66
67
68 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory) {
69 ExecutorType executorType = sessionFactory.getConfiguration().getDefaultExecutorType();
70 return getSqlSession(sessionFactory, executorType, null);
71 }
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
87
88 notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
89 notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
90
91 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
92
93 SqlSession session = sessionHolder(executorType, holder);
94 if (session != null) {
95 return session;
96 }
97
98 if (LOGGER.isDebugEnabled()) {
99 LOGGER.debug("Creating a new SqlSession");
100 }
101
102 session = sessionFactory.openSession(executorType);
103
104 registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
105
106 return session;
107 }
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
123 PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
124 SqlSessionHolder holder;
125 if (TransactionSynchronizationManager.isSynchronizationActive()) {
126 Environment environment = sessionFactory.getConfiguration().getEnvironment();
127
128 if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
129 if (LOGGER.isDebugEnabled()) {
130 LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
131 }
132
133 holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
134 TransactionSynchronizationManager.bindResource(sessionFactory, holder);
135 TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
136 holder.setSynchronizedWithTransaction(true);
137 holder.requested();
138 } else {
139 if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
140 if (LOGGER.isDebugEnabled()) {
141 LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
142 }
143 } else {
144 throw new TransientDataAccessResourceException(
145 "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
146 }
147 }
148 } else {
149 if (LOGGER.isDebugEnabled()) {
150 LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
151 }
152 }
153 }
154
155 private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
156 SqlSession session = null;
157 if (holder != null && holder.isSynchronizedWithTransaction()) {
158 if (holder.getExecutorType() != executorType) {
159 throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
160 }
161
162 holder.requested();
163
164 if (LOGGER.isDebugEnabled()) {
165 LOGGER.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
166 }
167
168 session = holder.getSqlSession();
169 }
170 return session;
171 }
172
173
174
175
176
177
178
179
180
181 public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
182 notNull(session, NO_SQL_SESSION_SPECIFIED);
183 notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
184
185 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
186 if ((holder != null) && (holder.getSqlSession() == session)) {
187 if (LOGGER.isDebugEnabled()) {
188 LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
189 }
190 holder.released();
191 } else {
192 if (LOGGER.isDebugEnabled()) {
193 LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
194 }
195 session.close();
196 }
197 }
198
199
200
201
202
203
204
205
206 public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
207 notNull(session, NO_SQL_SESSION_SPECIFIED);
208 notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
209
210 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
211
212 return (holder != null) && (holder.getSqlSession() == session);
213 }
214
215
216
217
218
219
220
221 private static final class SqlSessionSynchronization extends TransactionSynchronizationAdapter {
222
223 private final SqlSessionHolder holder;
224
225 private final SqlSessionFactory sessionFactory;
226
227 private boolean holderActive = true;
228
229 public SqlSessionSynchronization(SqlSessionHolder holder, SqlSessionFactory sessionFactory) {
230 notNull(holder, "Parameter 'holder' must be not null");
231 notNull(sessionFactory, "Parameter 'sessionFactory' must be not null");
232
233 this.holder = holder;
234 this.sessionFactory = sessionFactory;
235 }
236
237
238
239
240 @Override
241 public int getOrder() {
242
243 return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1;
244 }
245
246
247
248
249 @Override
250 public void suspend() {
251 if (this.holderActive) {
252 if (LOGGER.isDebugEnabled()) {
253 LOGGER.debug("Transaction synchronization suspending SqlSession [" + this.holder.getSqlSession() + "]");
254 }
255 TransactionSynchronizationManager.unbindResource(this.sessionFactory);
256 }
257 }
258
259
260
261
262 @Override
263 public void resume() {
264 if (this.holderActive) {
265 if (LOGGER.isDebugEnabled()) {
266 LOGGER.debug("Transaction synchronization resuming SqlSession [" + this.holder.getSqlSession() + "]");
267 }
268 TransactionSynchronizationManager.bindResource(this.sessionFactory, this.holder);
269 }
270 }
271
272
273
274
275 @Override
276 public void beforeCommit(boolean readOnly) {
277
278
279
280
281
282
283 if (TransactionSynchronizationManager.isActualTransactionActive()) {
284 try {
285 if (LOGGER.isDebugEnabled()) {
286 LOGGER.debug("Transaction synchronization committing SqlSession [" + this.holder.getSqlSession() + "]");
287 }
288 this.holder.getSqlSession().commit();
289 } catch (PersistenceException p) {
290 if (this.holder.getPersistenceExceptionTranslator() != null) {
291 DataAccessException translated = this.holder
292 .getPersistenceExceptionTranslator()
293 .translateExceptionIfPossible(p);
294 if (translated != null) {
295 throw translated;
296 }
297 }
298 throw p;
299 }
300 }
301 }
302
303
304
305
306 @Override
307 public void beforeCompletion() {
308
309
310 if (!this.holder.isOpen()) {
311 if (LOGGER.isDebugEnabled()) {
312 LOGGER.debug("Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
313 }
314 TransactionSynchronizationManager.unbindResource(sessionFactory);
315 this.holderActive = false;
316 if (LOGGER.isDebugEnabled()) {
317 LOGGER.debug("Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
318 }
319 this.holder.getSqlSession().close();
320 }
321 }
322
323
324
325
326 @Override
327 public void afterCompletion(int status) {
328 if (this.holderActive) {
329
330
331 if (LOGGER.isDebugEnabled()) {
332 LOGGER.debug("Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
333 }
334 TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory);
335 this.holderActive = false;
336 if (LOGGER.isDebugEnabled()) {
337 LOGGER.debug("Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
338 }
339 this.holder.getSqlSession().close();
340 }
341 this.holder.reset();
342 }
343 }
344
345 }