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