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