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.TransactionSynchronizationAdapter;
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 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
87
88
89
90
91 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
92 PersistenceExceptionTranslator exceptionTranslator) {
93
94 notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
95 notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
96
97 SqlSessionHolderrg/mybatis/spring/SqlSessionHolder.html#SqlSessionHolder">SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
98
99 SqlSession session = sessionHolder(executorType, holder);
100 if (session != null) {
101 return session;
102 }
103
104 LOGGER.debug(() -> "Creating a new SqlSession");
105 session = sessionFactory.openSession(executorType);
106
107 registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
108
109 return session;
110 }
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
129 PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
130 SqlSessionHolder holder;
131 if (TransactionSynchronizationManager.isSynchronizationActive()) {
132 Environment environment = sessionFactory.getConfiguration().getEnvironment();
133
134 if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
135 LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");
136
137 holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
138 TransactionSynchronizationManager.bindResource(sessionFactory, holder);
139 TransactionSynchronizationManager
140 .registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
141 holder.setSynchronizedWithTransaction(true);
142 holder.requested();
143 } else {
144 if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
145 LOGGER.debug(() -> "SqlSession [" + session
146 + "] was not registered for synchronization because DataSource is not transactional");
147 } else {
148 throw new TransientDataAccessResourceException(
149 "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
150 }
151 }
152 } else {
153 LOGGER.debug(() -> "SqlSession [" + session
154 + "] was not registered for synchronization because synchronization is not active");
155 }
156
157 }
158
159 private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
160 SqlSession session = null;
161 if (holder != null && holder.isSynchronizedWithTransaction()) {
162 if (holder.getExecutorType() != executorType) {
163 throw new TransientDataAccessResourceException(
164 "Cannot change the ExecutorType when there is an existing transaction");
165 }
166
167 holder.requested();
168
169 LOGGER.debug(() -> "Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
170 session = holder.getSqlSession();
171 }
172 return session;
173 }
174
175
176
177
178
179
180
181
182
183
184
185 public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
186 notNull(session, NO_SQL_SESSION_SPECIFIED);
187 notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
188
189 SqlSessionHolderrg/mybatis/spring/SqlSessionHolder.html#SqlSessionHolder">SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
190 if ((holder != null) && (holder.getSqlSession() == session)) {
191 LOGGER.debug(() -> "Releasing transactional SqlSession [" + session + "]");
192 holder.released();
193 } else {
194 LOGGER.debug(() -> "Closing non transactional SqlSession [" + session + "]");
195 session.close();
196 }
197 }
198
199
200
201
202
203
204
205
206
207
208 public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
209 notNull(session, NO_SQL_SESSION_SPECIFIED);
210 notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
211
212 SqlSessionHolderrg/mybatis/spring/SqlSessionHolder.html#SqlSessionHolder">SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
213
214 return (holder != null) && (holder.getSqlSession() == session);
215 }
216
217
218
219
220
221
222 private static final class SqlSessionSynchronization extends TransactionSynchronizationAdapter {
223
224 private final SqlSessionHolder holder;
225
226 private final SqlSessionFactory sessionFactory;
227
228 private boolean holderActive = true;
229
230 public SqlSessionSynchronization(SqlSessionHolder holder, SqlSessionFactory sessionFactory) {
231 notNull(holder, "Parameter 'holder' must be not null");
232 notNull(sessionFactory, "Parameter 'sessionFactory' must be not null");
233
234 this.holder = holder;
235 this.sessionFactory = sessionFactory;
236 }
237
238
239
240
241 @Override
242 public int getOrder() {
243
244 return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1;
245 }
246
247
248
249
250 @Override
251 public void suspend() {
252 if (this.holderActive) {
253 LOGGER.debug(() -> "Transaction synchronization suspending SqlSession [" + this.holder.getSqlSession() + "]");
254 TransactionSynchronizationManager.unbindResource(this.sessionFactory);
255 }
256 }
257
258
259
260
261 @Override
262 public void resume() {
263 if (this.holderActive) {
264 LOGGER.debug(() -> "Transaction synchronization resuming SqlSession [" + this.holder.getSqlSession() + "]");
265 TransactionSynchronizationManager.bindResource(this.sessionFactory, this.holder);
266 }
267 }
268
269
270
271
272 @Override
273 public void beforeCommit(boolean readOnly) {
274
275
276
277
278
279
280 if (TransactionSynchronizationManager.isActualTransactionActive()) {
281 try {
282 LOGGER.debug(() -> "Transaction synchronization committing SqlSession [" + this.holder.getSqlSession() + "]");
283 this.holder.getSqlSession().commit();
284 } catch (PersistenceException p) {
285 if (this.holder.getPersistenceExceptionTranslator() != null) {
286 DataAccessException translated = this.holder.getPersistenceExceptionTranslator()
287 .translateExceptionIfPossible(p);
288 if (translated != null) {
289 throw translated;
290 }
291 }
292 throw p;
293 }
294 }
295 }
296
297
298
299
300 @Override
301 public void beforeCompletion() {
302
303
304 if (!this.holder.isOpen()) {
305 LOGGER
306 .debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
307 TransactionSynchronizationManager.unbindResource(sessionFactory);
308 this.holderActive = false;
309 LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
310 this.holder.getSqlSession().close();
311 }
312 }
313
314
315
316
317 @Override
318 public void afterCompletion(int status) {
319 if (this.holderActive) {
320
321
322 LOGGER
323 .debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
324 TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory);
325 this.holderActive = false;
326 LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
327 this.holder.getSqlSession().close();
328 }
329 this.holder.reset();
330 }
331 }
332
333 }