• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / spring / #1227

14 Sep 2023 05:15PM CUT coverage: 89.622%. Remained the same
#1227

Pull #854

github

web-flow
Update dependency net.bytebuddy:byte-buddy-agent to v1.14.8
Pull Request #854: Update dependency net.bytebuddy:byte-buddy-agent to v1.14.8

829 of 925 relevant lines covered (89.62%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.67
/src/main/java/org/mybatis/spring/SqlSessionTemplate.java
1
/*
2
 * Copyright 2010-2022 the original author or authors.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *    https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package org.mybatis.spring;
17

18
import static java.lang.reflect.Proxy.newProxyInstance;
19

20
import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
21
import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;
22
import static org.mybatis.spring.SqlSessionUtils.getSqlSession;
23
import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;
24
import static org.springframework.util.Assert.notNull;
25

26
import java.lang.reflect.InvocationHandler;
27
import java.lang.reflect.Method;
28
import java.sql.Connection;
29
import java.util.List;
30
import java.util.Map;
31

32
import org.apache.ibatis.cursor.Cursor;
33
import org.apache.ibatis.exceptions.PersistenceException;
34
import org.apache.ibatis.executor.BatchResult;
35
import org.apache.ibatis.session.Configuration;
36
import org.apache.ibatis.session.ExecutorType;
37
import org.apache.ibatis.session.ResultHandler;
38
import org.apache.ibatis.session.RowBounds;
39
import org.apache.ibatis.session.SqlSession;
40
import org.apache.ibatis.session.SqlSessionFactory;
41
import org.springframework.beans.factory.DisposableBean;
42
import org.springframework.dao.support.PersistenceExceptionTranslator;
43

44
/**
45
 * Thread safe, Spring managed, {@code SqlSession} that works with Spring transaction management to ensure that the
46
 * actual SqlSession used is the one associated with the current Spring transaction. In addition, it manages the session
47
 * life-cycle, including closing, committing or rolling back the session as necessary based on the Spring transaction
48
 * configuration.
49
 * <p>
50
 * The template needs a SqlSessionFactory to create SqlSessions, passed as a constructor argument. It also can be
51
 * constructed indicating the executor type to be used, if not, the default executor type, defined in the session
52
 * factory will be used.
53
 * <p>
54
 * This template converts MyBatis PersistenceExceptions into unchecked DataAccessExceptions, using, by default, a
55
 * {@code MyBatisExceptionTranslator}.
56
 * <p>
57
 * Because SqlSessionTemplate is thread safe, a single instance can be shared by all DAOs; there should also be a small
58
 * memory savings by doing this. This pattern can be used in Spring configuration files as follows:
59
 *
60
 * <pre class="code">
61
 * {@code
62
 * <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
63
 *   <constructor-arg ref="sqlSessionFactory" />
64
 * </bean>
65
 * }
66
 * </pre>
67
 *
68
 * @author Putthiphong Boonphong
69
 * @author Hunter Presnall
70
 * @author Eduardo Macarron
71
 *
72
 * @see SqlSessionFactory
73
 * @see MyBatisExceptionTranslator
74
 */
75
public class SqlSessionTemplate implements SqlSession, DisposableBean {
76

77
  private final SqlSessionFactory sqlSessionFactory;
78

79
  private final ExecutorType executorType;
80

81
  private final SqlSession sqlSessionProxy;
82

83
  private final PersistenceExceptionTranslator exceptionTranslator;
84

85
  /**
86
   * Constructs a Spring managed SqlSession with the {@code SqlSessionFactory} provided as an argument.
87
   *
88
   * @param sqlSessionFactory
89
   *          a factory of SqlSession
90
   */
91
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
92
    this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
1✔
93
  }
1✔
94

95
  /**
96
   * Constructs a Spring managed SqlSession with the {@code SqlSessionFactory} provided as an argument and the given
97
   * {@code ExecutorType} {@code ExecutorType} cannot be changed once the {@code SqlSessionTemplate} is constructed.
98
   *
99
   * @param sqlSessionFactory
100
   *          a factory of SqlSession
101
   * @param executorType
102
   *          an executor type on session
103
   */
104
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
105
    this(sqlSessionFactory, executorType,
1✔
106
        new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
1✔
107
  }
1✔
108

109
  /**
110
   * Constructs a Spring managed {@code SqlSession} with the given {@code SqlSessionFactory} and {@code ExecutorType}. A
111
   * custom {@code SQLExceptionTranslator} can be provided as an argument so any {@code PersistenceException} thrown by
112
   * MyBatis can be custom translated to a {@code RuntimeException} The {@code SQLExceptionTranslator} can also be null
113
   * and thus no exception translation will be done and MyBatis exceptions will be thrown
114
   *
115
   * @param sqlSessionFactory
116
   *          a factory of SqlSession
117
   * @param executorType
118
   *          an executor type on session
119
   * @param exceptionTranslator
120
   *          a translator of exception
121
   */
122
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
123
      PersistenceExceptionTranslator exceptionTranslator) {
1✔
124

125
    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
1✔
126
    notNull(executorType, "Property 'executorType' is required");
1✔
127

128
    this.sqlSessionFactory = sqlSessionFactory;
1✔
129
    this.executorType = executorType;
1✔
130
    this.exceptionTranslator = exceptionTranslator;
1✔
131
    this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
1✔
132
        new Class[] { SqlSession.class }, new SqlSessionInterceptor());
133
  }
1✔
134

135
  public SqlSessionFactory getSqlSessionFactory() {
136
    return this.sqlSessionFactory;
1✔
137
  }
138

139
  public ExecutorType getExecutorType() {
140
    return this.executorType;
1✔
141
  }
142

143
  public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
144
    return this.exceptionTranslator;
×
145
  }
146

147
  /**
148
   * {@inheritDoc}
149
   */
150
  @Override
151
  public <T> T selectOne(String statement) {
152
    return this.sqlSessionProxy.selectOne(statement);
1✔
153
  }
154

155
  /**
156
   * {@inheritDoc}
157
   */
158
  @Override
159
  public <T> T selectOne(String statement, Object parameter) {
160
    return this.sqlSessionProxy.selectOne(statement, parameter);
1✔
161
  }
162

163
  /**
164
   * {@inheritDoc}
165
   */
166
  @Override
167
  public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
168
    return this.sqlSessionProxy.selectMap(statement, mapKey);
×
169
  }
170

171
  /**
172
   * {@inheritDoc}
173
   */
174
  @Override
175
  public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
176
    return this.sqlSessionProxy.selectMap(statement, parameter, mapKey);
×
177
  }
178

179
  /**
180
   * {@inheritDoc}
181
   */
182
  @Override
183
  public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
184
    return this.sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds);
×
185
  }
186

187
  /**
188
   * {@inheritDoc}
189
   */
190
  @Override
191
  public <T> Cursor<T> selectCursor(String statement) {
192
    return this.sqlSessionProxy.selectCursor(statement);
×
193
  }
194

195
  /**
196
   * {@inheritDoc}
197
   */
198
  @Override
199
  public <T> Cursor<T> selectCursor(String statement, Object parameter) {
200
    return this.sqlSessionProxy.selectCursor(statement, parameter);
×
201
  }
202

203
  /**
204
   * {@inheritDoc}
205
   */
206
  @Override
207
  public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
208
    return this.sqlSessionProxy.selectCursor(statement, parameter, rowBounds);
×
209
  }
210

211
  /**
212
   * {@inheritDoc}
213
   */
214
  @Override
215
  public <E> List<E> selectList(String statement) {
216
    return this.sqlSessionProxy.selectList(statement);
×
217
  }
218

219
  /**
220
   * {@inheritDoc}
221
   */
222
  @Override
223
  public <E> List<E> selectList(String statement, Object parameter) {
224
    return this.sqlSessionProxy.selectList(statement, parameter);
1✔
225
  }
226

227
  /**
228
   * {@inheritDoc}
229
   */
230
  @Override
231
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
232
    return this.sqlSessionProxy.selectList(statement, parameter, rowBounds);
×
233
  }
234

235
  /**
236
   * {@inheritDoc}
237
   */
238
  @Override
239
  public void select(String statement, ResultHandler handler) {
240
    this.sqlSessionProxy.select(statement, handler);
×
241
  }
×
242

243
  /**
244
   * {@inheritDoc}
245
   */
246
  @Override
247
  public void select(String statement, Object parameter, ResultHandler handler) {
248
    this.sqlSessionProxy.select(statement, parameter, handler);
×
249
  }
×
250

251
  /**
252
   * {@inheritDoc}
253
   */
254
  @Override
255
  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
256
    this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
×
257
  }
×
258

259
  /**
260
   * {@inheritDoc}
261
   */
262
  @Override
263
  public int insert(String statement) {
264
    return this.sqlSessionProxy.insert(statement);
×
265
  }
266

267
  /**
268
   * {@inheritDoc}
269
   */
270
  @Override
271
  public int insert(String statement, Object parameter) {
272
    return this.sqlSessionProxy.insert(statement, parameter);
1✔
273
  }
274

275
  /**
276
   * {@inheritDoc}
277
   */
278
  @Override
279
  public int update(String statement) {
280
    return this.sqlSessionProxy.update(statement);
×
281
  }
282

283
  /**
284
   * {@inheritDoc}
285
   */
286
  @Override
287
  public int update(String statement, Object parameter) {
288
    return this.sqlSessionProxy.update(statement, parameter);
1✔
289
  }
290

291
  /**
292
   * {@inheritDoc}
293
   */
294
  @Override
295
  public int delete(String statement) {
296
    return this.sqlSessionProxy.delete(statement);
×
297
  }
298

299
  /**
300
   * {@inheritDoc}
301
   */
302
  @Override
303
  public int delete(String statement, Object parameter) {
304
    return this.sqlSessionProxy.delete(statement, parameter);
×
305
  }
306

307
  /**
308
   * {@inheritDoc}
309
   */
310
  @Override
311
  public <T> T getMapper(Class<T> type) {
312
    return getConfiguration().getMapper(type, this);
1✔
313
  }
314

315
  /**
316
   * {@inheritDoc}
317
   */
318
  @Override
319
  public void commit() {
320
    throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
1✔
321
  }
322

323
  /**
324
   * {@inheritDoc}
325
   */
326
  @Override
327
  public void commit(boolean force) {
328
    throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
×
329
  }
330

331
  /**
332
   * {@inheritDoc}
333
   */
334
  @Override
335
  public void rollback() {
336
    throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
1✔
337
  }
338

339
  /**
340
   * {@inheritDoc}
341
   */
342
  @Override
343
  public void rollback(boolean force) {
344
    throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
×
345
  }
346

347
  /**
348
   * {@inheritDoc}
349
   */
350
  @Override
351
  public void close() {
352
    throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
1✔
353
  }
354

355
  /**
356
   * {@inheritDoc}
357
   */
358
  @Override
359
  public void clearCache() {
360
    this.sqlSessionProxy.clearCache();
×
361
  }
×
362

363
  /**
364
   * {@inheritDoc}
365
   */
366
  @Override
367
  public Configuration getConfiguration() {
368
    return this.sqlSessionFactory.getConfiguration();
1✔
369
  }
370

371
  /**
372
   * {@inheritDoc}
373
   */
374
  @Override
375
  public Connection getConnection() {
376
    return this.sqlSessionProxy.getConnection();
1✔
377
  }
378

379
  /**
380
   * {@inheritDoc}
381
   *
382
   * @since 1.0.2
383
   */
384
  @Override
385
  public List<BatchResult> flushStatements() {
386
    return this.sqlSessionProxy.flushStatements();
1✔
387
  }
388

389
  /**
390
   * Allow gently dispose bean:
391
   *
392
   * <pre>
393
   * {@code
394
   *
395
   * <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
396
   *  <constructor-arg index="0" ref="sqlSessionFactory" />
397
   * </bean>
398
   * }
399
   * </pre>
400
   *
401
   * The implementation of {@link DisposableBean} forces spring context to use {@link DisposableBean#destroy()} method
402
   * instead of {@link SqlSessionTemplate#close()} to shutdown gently.
403
   *
404
   * @see SqlSessionTemplate#close()
405
   * @see "org.springframework.beans.factory.support.DisposableBeanAdapter#inferDestroyMethodIfNecessary(Object, RootBeanDefinition)"
406
   * @see "org.springframework.beans.factory.support.DisposableBeanAdapter#CLOSE_METHOD_NAME"
407
   */
408
  @Override
409
  public void destroy() throws Exception {
410
    // This method forces spring disposer to avoid call of SqlSessionTemplate.close() which gives
411
    // UnsupportedOperationException
412
  }
1✔
413

414
  /**
415
   * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
416
   * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to the
417
   * {@code PersistenceExceptionTranslator}.
418
   */
419
  private class SqlSessionInterceptor implements InvocationHandler {
1✔
420
    @Override
421
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
422
      SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
1✔
423
          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
424
      try {
425
        Object result = method.invoke(sqlSession, args);
1✔
426
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
1✔
427
          // force commit even on non-dirty sessions because some databases require
428
          // a commit/rollback before calling close()
429
          sqlSession.commit(true);
1✔
430
        }
431
        return result;
1✔
432
      } catch (Throwable t) {
1✔
433
        Throwable unwrapped = unwrapThrowable(t);
1✔
434
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
1✔
435
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
436
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
1✔
437
          sqlSession = null;
1✔
438
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator
1✔
439
              .translateExceptionIfPossible((PersistenceException) unwrapped);
1✔
440
          if (translated != null) {
1✔
441
            unwrapped = translated;
1✔
442
          }
443
        }
444
        throw unwrapped;
1✔
445
      } finally {
446
        if (sqlSession != null) {
1✔
447
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
1✔
448
        }
449
      }
450
    }
451
  }
452

453
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc