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

mybatis / spring / 2076

17 Oct 2026 04:20PM UTC coverage: 90.616% (+0.3%) from 90.335%
2076

push

github

web-flow
Update dependency maven to v3.10.0

323 of 386 branches covered (83.68%)

956 of 1055 relevant lines covered (90.62%)

0.91 hits per line

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

90.76
/src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java
1
/*
2
 * Copyright 2010-2026 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 org.springframework.util.Assert.notNull;
19
import static org.springframework.util.Assert.state;
20
import static org.springframework.util.ObjectUtils.isEmpty;
21
import static org.springframework.util.StringUtils.hasLength;
22
import static org.springframework.util.StringUtils.tokenizeToStringArray;
23

24
import java.io.IOException;
25
import java.io.InputStream;
26
import java.lang.reflect.Modifier;
27
import java.sql.SQLException;
28
import java.util.ArrayList;
29
import java.util.Arrays;
30
import java.util.HashSet;
31
import java.util.List;
32
import java.util.Optional;
33
import java.util.Properties;
34
import java.util.Set;
35
import java.util.function.IntFunction;
36
import java.util.stream.Stream;
37

38
import javax.sql.DataSource;
39

40
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
41
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
42
import org.apache.ibatis.cache.Cache;
43
import org.apache.ibatis.executor.ErrorContext;
44
import org.apache.ibatis.io.Resources;
45
import org.apache.ibatis.io.VFS;
46
import org.apache.ibatis.mapping.DatabaseIdProvider;
47
import org.apache.ibatis.mapping.Environment;
48
import org.apache.ibatis.plugin.Interceptor;
49
import org.apache.ibatis.reflection.factory.ObjectFactory;
50
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
51
import org.apache.ibatis.scripting.LanguageDriver;
52
import org.apache.ibatis.session.Configuration;
53
import org.apache.ibatis.session.SqlSessionFactory;
54
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
55
import org.apache.ibatis.transaction.TransactionFactory;
56
import org.apache.ibatis.type.TypeHandler;
57
import org.mybatis.logging.Logger;
58
import org.mybatis.logging.LoggerFactory;
59
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
60
import org.springframework.beans.factory.FactoryBean;
61
import org.springframework.beans.factory.InitializingBean;
62
import org.springframework.context.ApplicationListener;
63
import org.springframework.context.ConfigurableApplicationContext;
64
import org.springframework.context.event.ContextRefreshedEvent;
65
import org.springframework.core.io.Resource;
66
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
67
import org.springframework.core.io.support.ResourcePatternResolver;
68
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
69
import org.springframework.core.type.classreading.MetadataReaderFactory;
70
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
71
import org.springframework.util.ClassUtils;
72

73
/**
74
 * {@code FactoryBean} that creates a MyBatis {@code SqlSessionFactory}. This is the usual way to set up a shared
75
 * MyBatis {@code SqlSessionFactory} in a Spring application context; the SqlSessionFactory can then be passed to
76
 * MyBatis-based DAOs via dependency injection.
77
 * <p>
78
 * Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager} can be used for transaction demarcation
79
 * in combination with a {@code SqlSessionFactory}. JTA should be used for transactions which span multiple databases or
80
 * when container managed transactions (CMT) are being used.
81
 *
82
 * @author Putthiphong Boonphong
83
 * @author Hunter Presnall
84
 * @author Eduardo Macarron
85
 * @author EddĂș MelĂ©ndez
86
 * @author Kazuki Shimizu
87
 * @author Jens Schauder
88
 *
89
 * @see #setConfigLocation
90
 * @see #setDataSource
91
 */
92
public class SqlSessionFactoryBean
1 ✔
93
    implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ContextRefreshedEvent> {
94

95
  private static final Logger LOGGER = LoggerFactory.getLogger(SqlSessionFactoryBean.class);
1 ✔
96

97
  private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();
1 ✔
98
  private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();
1 ✔
99

100
  private Resource configLocation;
101

102
  private Configuration configuration;
103

104
  private Resource[] mapperLocations;
105

106
  private DataSource dataSource;
107

108
  private TransactionFactory transactionFactory;
109

110
  private Properties configurationProperties;
111

112
  private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
1 ✔
113

114
  private SqlSessionFactory sqlSessionFactory;
115

116
  // EnvironmentAware requires spring 3.1
117
  private String environment = SqlSessionFactoryBean.class.getSimpleName();
1 ✔
118

119
  private boolean failFast;
120

121
  private Interceptor[] plugins;
122

123
  private TypeHandler<?>[] typeHandlers;
124

125
  private String typeHandlersPackage;
126

127
  @SuppressWarnings("rawtypes")
128
  private Class<? extends TypeHandler> defaultEnumTypeHandler;
129

130
  private Class<?>[] typeAliases;
131

132
  private String typeAliasesPackage;
133

134
  private Class<?> typeAliasesSuperType;
135

136
  private LanguageDriver[] scriptingLanguageDrivers;
137

138
  private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;
139

140
  // issue #19. No default provider.
141
  private DatabaseIdProvider databaseIdProvider;
142

143
  private Class<? extends VFS> vfs;
144

145
  private Cache cache;
146

147
  private ObjectFactory objectFactory;
148

149
  private ObjectWrapperFactory objectWrapperFactory;
150

151
  /**
152
   * Sets the ObjectFactory.
153
   *
154
   * @since 1.1.2
155
   *
156
   * @param objectFactory
157
   *          a custom ObjectFactory
158
   */
159
  public void setObjectFactory(ObjectFactory objectFactory) {
160
    this.objectFactory = objectFactory;
1 ✔
161
  }
1 ✔
162

163
  /**
164
   * Sets the ObjectWrapperFactory.
165
   *
166
   * @since 1.1.2
167
   *
168
   * @param objectWrapperFactory
169
   *          a specified ObjectWrapperFactory
170
   */
171
  public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
172
    this.objectWrapperFactory = objectWrapperFactory;
1 ✔
173
  }
1 ✔
174

175
  /**
176
   * Gets the DatabaseIdProvider
177
   *
178
   * @since 1.1.0
179
   *
180
   * @return a specified DatabaseIdProvider
181
   */
182
  public DatabaseIdProvider getDatabaseIdProvider() {
183
    return databaseIdProvider;
×
184
  }
185

186
  /**
187
   * Sets the DatabaseIdProvider. As of version 1.2.2 this variable is not initialized by default.
188
   *
189
   * @since 1.1.0
190
   *
191
   * @param databaseIdProvider
192
   *          a DatabaseIdProvider
193
   */
194
  public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
195
    this.databaseIdProvider = databaseIdProvider;
1 ✔
196
  }
1 ✔
197

198
  /**
199
   * Gets the VFS.
200
   *
201
   * @return a specified VFS
202
   */
203
  public Class<? extends VFS> getVfs() {
204
    return this.vfs;
×
205
  }
206

207
  /**
208
   * Sets the VFS.
209
   *
210
   * @param vfs
211
   *          a VFS
212
   */
213
  public void setVfs(Class<? extends VFS> vfs) {
214
    this.vfs = vfs;
×
215
  }
×
216

217
  /**
218
   * Gets the Cache.
219
   *
220
   * @return a specified Cache
221
   */
222
  public Cache getCache() {
223
    return this.cache;
1 ✔
224
  }
225

226
  /**
227
   * Sets the Cache.
228
   *
229
   * @param cache
230
   *          a Cache
231
   */
232
  public void setCache(Cache cache) {
233
    this.cache = cache;
1 ✔
234
  }
1 ✔
235

236
  /**
237
   * Mybatis plugin list.
238
   *
239
   * @since 1.0.1
240
   *
241
   * @param plugins
242
   *          list of plugins
243
   */
244
  public void setPlugins(Interceptor... plugins) {
245
    this.plugins = plugins;
1 ✔
246
  }
1 ✔
247

248
  /**
249
   * Packages to search for type aliases.
250
   * <p>
251
   * Since 2.0.1, allow to specify a wildcard such as {@code com.example.*.model}.
252
   *
253
   * @since 1.0.1
254
   *
255
   * @param typeAliasesPackage
256
   *          package to scan for domain objects
257
   */
258
  public void setTypeAliasesPackage(String typeAliasesPackage) {
259
    this.typeAliasesPackage = typeAliasesPackage;
1 ✔
260
  }
1 ✔
261

262
  /**
263
   * Super class which domain objects have to extend to have a type alias created. No effect if there is no package to
264
   * scan configured.
265
   *
266
   * @since 1.1.2
267
   *
268
   * @param typeAliasesSuperType
269
   *          super class for domain objects
270
   */
271
  public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
272
    this.typeAliasesSuperType = typeAliasesSuperType;
1 ✔
273
  }
1 ✔
274

275
  /**
276
   * Packages to search for type handlers.
277
   * <p>
278
   * Since 2.0.1, allow to specify a wildcard such as {@code com.example.*.typehandler}.
279
   *
280
   * @since 1.0.1
281
   *
282
   * @param typeHandlersPackage
283
   *          package to scan for type handlers
284
   */
285
  public void setTypeHandlersPackage(String typeHandlersPackage) {
286
    this.typeHandlersPackage = typeHandlersPackage;
1 ✔
287
  }
1 ✔
288

289
  /**
290
   * Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes}
291
   *
292
   * @since 1.0.1
293
   *
294
   * @param typeHandlers
295
   *          Type handler list
296
   */
297
  public void setTypeHandlers(TypeHandler<?>... typeHandlers) {
298
    this.typeHandlers = typeHandlers;
1 ✔
299
  }
1 ✔
300

301
  /**
302
   * Set the default type handler class for enum.
303
   *
304
   * @since 2.0.5
305
   *
306
   * @param defaultEnumTypeHandler
307
   *          The default type handler class for enum
308
   */
309
  public void setDefaultEnumTypeHandler(
310
      @SuppressWarnings("rawtypes") Class<? extends TypeHandler> defaultEnumTypeHandler) {
311
    this.defaultEnumTypeHandler = defaultEnumTypeHandler;
1 ✔
312
  }
1 ✔
313

314
  /**
315
   * List of type aliases to register. They can be annotated with {@code Alias}
316
   *
317
   * @since 1.0.1
318
   *
319
   * @param typeAliases
320
   *          Type aliases list
321
   */
322
  public void setTypeAliases(Class<?>... typeAliases) {
323
    this.typeAliases = typeAliases;
1 ✔
324
  }
1 ✔
325

326
  /**
327
   * If true, a final check is done on Configuration to assure that all mapped statements are fully loaded and there is
328
   * no one still pending to resolve includes. Defaults to false.
329
   *
330
   * @since 1.0.1
331
   *
332
   * @param failFast
333
   *          enable failFast
334
   */
335
  public void setFailFast(boolean failFast) {
336
    this.failFast = failFast;
×
337
  }
×
338

339
  /**
340
   * Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is
341
   * "WEB-INF/mybatis-configuration.xml".
342
   *
343
   * @param configLocation
344
   *          a location the MyBatis config file
345
   */
346
  public void setConfigLocation(Resource configLocation) {
347
    this.configLocation = configLocation;
1 ✔
348
  }
1 ✔
349

350
  /**
351
   * Set a customized MyBatis configuration.
352
   *
353
   * @param configuration
354
   *          MyBatis configuration
355
   *
356
   * @since 1.3.0
357
   */
358
  public void setConfiguration(Configuration configuration) {
359
    this.configuration = configuration;
1 ✔
360
  }
1 ✔
361

362
  /**
363
   * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} configuration
364
   * at runtime.
365
   * <p>
366
   * This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file. This property being
367
   * based on Spring's resource abstraction also allows for specifying resource patterns here: e.g.
368
   * "classpath*:sqlmap/*-mapper.xml".
369
   *
370
   * @param mapperLocations
371
   *          location of MyBatis mapper files
372
   */
373
  public void setMapperLocations(Resource... mapperLocations) {
374
    this.mapperLocations = mapperLocations;
1 ✔
375
  }
1 ✔
376

377
  /**
378
   * Set optional properties to be passed into the SqlSession configuration, as alternative to a
379
   * {@code &lt;properties&gt;} tag in the configuration xml file. This will be used to resolve placeholders in the
380
   * config file.
381
   *
382
   * @param sqlSessionFactoryProperties
383
   *          optional properties for the SqlSessionFactory
384
   */
385
  public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {
386
    this.configurationProperties = sqlSessionFactoryProperties;
1 ✔
387
  }
1 ✔
388

389
  /**
390
   * Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource} should
391
   * match the one used by the {@code SqlSessionFactory}: for example, you could specify the same JNDI DataSource for
392
   * both.
393
   * <p>
394
   * A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code accessing
395
   * this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}.
396
   * <p>
397
   * The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not a
398
   * {@code TransactionAwareDataSourceProxy}. Only data access code may work with
399
   * {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the underlying target
400
   * {@code DataSource}. If there's nevertheless a {@code TransactionAwareDataSourceProxy} passed in, it will be
401
   * unwrapped to extract its target {@code DataSource}.
402
   *
403
   * @param dataSource
404
   *          a JDBC {@code DataSource}
405
   */
406
  public void setDataSource(DataSource dataSource) {
407
    if (dataSource instanceof TransactionAwareDataSourceProxy) {
1 !
408
      // If we got a TransactionAwareDataSourceProxy, we need to perform
409
      // transactions for its underlying target DataSource, else data
410
      // access code won't see properly exposed transactions (i.e.
411
      // transactions for the target DataSource).
412
      this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
×
413
    } else {
414
      this.dataSource = dataSource;
1 ✔
415
    }
416
  }
1 ✔
417

418
  /**
419
   * Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}.
420
   * <p>
421
   * This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By default,
422
   * {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances.
423
   *
424
   * @param sqlSessionFactoryBuilder
425
   *          a SqlSessionFactoryBuilder
426
   */
427
  public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
428
    this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
1 ✔
429
  }
1 ✔
430

431
  /**
432
   * Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory}.
433
   * <p>
434
   * The default {@code SpringManagedTransactionFactory} should be appropriate for all cases: be it Spring transaction
435
   * management, EJB CMT or plain JTA. If there is no active transaction, SqlSession operations will execute SQL
436
   * statements non-transactionally.
437
   * <p>
438
   * <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> If not used, any attempt at
439
   * getting an SqlSession through Spring's MyBatis framework will throw an exception if a transaction is active.
440
   *
441
   * @see SpringManagedTransactionFactory
442
   *
443
   * @param transactionFactory
444
   *          the MyBatis TransactionFactory
445
   */
446
  public void setTransactionFactory(TransactionFactory transactionFactory) {
447
    this.transactionFactory = transactionFactory;
1 ✔
448
  }
1 ✔
449

450
  /**
451
   * <b>NOTE:</b> This class <em>overrides</em> any {@code Environment} you have set in the MyBatis config file. This is
452
   * used only as a placeholder name. The default value is {@code SqlSessionFactoryBean.class.getSimpleName()}.
453
   *
454
   * @param environment
455
   *          the environment name
456
   */
457
  public void setEnvironment(String environment) {
458
    this.environment = environment;
1 ✔
459
  }
1 ✔
460

461
  /**
462
   * Set scripting language drivers.
463
   *
464
   * @param scriptingLanguageDrivers
465
   *          scripting language drivers
466
   *
467
   * @since 2.0.2
468
   */
469
  public void setScriptingLanguageDrivers(LanguageDriver... scriptingLanguageDrivers) {
470
    this.scriptingLanguageDrivers = scriptingLanguageDrivers;
1 ✔
471
  }
1 ✔
472

473
  /**
474
   * Set a default scripting language driver class.
475
   *
476
   * @param defaultScriptingLanguageDriver
477
   *          A default scripting language driver class
478
   *
479
   * @since 2.0.2
480
   */
481
  public void setDefaultScriptingLanguageDriver(Class<? extends LanguageDriver> defaultScriptingLanguageDriver) {
482
    this.defaultScriptingLanguageDriver = defaultScriptingLanguageDriver;
1 ✔
483
  }
1 ✔
484

485
  /**
486
   * Add locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} configuration
487
   * at runtime.
488
   * <p>
489
   * This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file. This property being
490
   * based on Spring's resource abstraction also allows for specifying resource patterns here: e.g.
491
   * "classpath*:sqlmap/*-mapper.xml".
492
   *
493
   * @param mapperLocations
494
   *          location of MyBatis mapper files
495
   *
496
   * @see #setMapperLocations(Resource...)
497
   *
498
   * @since 3.0.2
499
   */
500
  public void addMapperLocations(Resource... mapperLocations) {
501
    setMapperLocations(appendArrays(this.mapperLocations, mapperLocations, Resource[]::new));
1 ✔
502
  }
1 ✔
503

504
  /**
505
   * Add type handlers.
506
   *
507
   * @param typeHandlers
508
   *          Type handler list
509
   *
510
   * @since 3.0.2
511
   */
512
  public void addTypeHandlers(TypeHandler<?>... typeHandlers) {
513
    setTypeHandlers(appendArrays(this.typeHandlers, typeHandlers, TypeHandler[]::new));
1 ✔
514
  }
1 ✔
515

516
  /**
517
   * Add scripting language drivers.
518
   *
519
   * @param scriptingLanguageDrivers
520
   *          scripting language drivers
521
   *
522
   * @since 3.0.2
523
   */
524
  public void addScriptingLanguageDrivers(LanguageDriver... scriptingLanguageDrivers) {
525
    setScriptingLanguageDrivers(
1 ✔
526
        appendArrays(this.scriptingLanguageDrivers, scriptingLanguageDrivers, LanguageDriver[]::new));
1 ✔
527
  }
1 ✔
528

529
  /**
530
   * Add Mybatis plugins.
531
   *
532
   * @param plugins
533
   *          list of plugins
534
   *
535
   * @since 3.0.2
536
   */
537
  public void addPlugins(Interceptor... plugins) {
538
    setPlugins(appendArrays(this.plugins, plugins, Interceptor[]::new));
1 ✔
539
  }
1 ✔
540

541
  /**
542
   * Add type aliases.
543
   *
544
   * @param typeAliases
545
   *          Type aliases list
546
   *
547
   * @since 3.0.2
548
   */
549
  public void addTypeAliases(Class<?>... typeAliases) {
550
    setTypeAliases(appendArrays(this.typeAliases, typeAliases, Class[]::new));
1 ✔
551
  }
1 ✔
552

553
  private <T> T[] appendArrays(T[] oldArrays, T[] newArrays, IntFunction<T[]> generator) {
554
    if (oldArrays == null) {
1 ✔
555
      return newArrays;
1 ✔
556
    }
557
    if (newArrays == null) {
1 ✔
558
      return oldArrays;
1 ✔
559
    }
560
    List<T> newList = new ArrayList<>(Arrays.asList(oldArrays));
1 ✔
561
    newList.addAll(Arrays.asList(newArrays));
1 ✔
562
    return newList.toArray(generator.apply(0));
1 ✔
563
  }
564

565
  @Override
566
  public void afterPropertiesSet() throws Exception {
567
    notNull(dataSource, "Property 'dataSource' is required");
1 ✔
568
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
1 ✔
569
    state(configuration == null || configLocation == null,
1 ✔
570
        "Only one of 'configuration' or 'configLocation' may be specified");
571

572
    this.sqlSessionFactory = buildSqlSessionFactory();
1 ✔
573
  }
1 ✔
574

575
  /**
576
   * Build a {@code SqlSessionFactory} instance.
577
   * <p>
578
   * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
579
   * {@code SqlSessionFactory} instance based on a Reader. Since 1.3.0, it can be specified a {@link Configuration}
580
   * instance directly(without config file).
581
   *
582
   * @return SqlSessionFactory
583
   *
584
   * @throws Exception
585
   *           if configuration is failed
586
   */
587
  protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
588

589
    final Configuration targetConfiguration;
590

591
    XMLConfigBuilder xmlConfigBuilder = null;
1 ✔
592
    if (this.configuration != null) {
1 ✔
593
      targetConfiguration = this.configuration;
1 ✔
594
      if (targetConfiguration.getVariables() == null) {
1 ✔
595
        targetConfiguration.setVariables(this.configurationProperties);
1 ✔
596
      } else if (this.configurationProperties != null) {
1 ✔
597
        targetConfiguration.getVariables().putAll(this.configurationProperties);
1 ✔
598
      }
599
    } else if (this.configLocation != null) {
1 ✔
600
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
1 ✔
601
      targetConfiguration = xmlConfigBuilder.getConfiguration();
1 ✔
602
    } else {
603
      LOGGER.debug(
1 ✔
604
          () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
×
605
      targetConfiguration = new Configuration();
1 ✔
606
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
1 ✔
607
    }
608

609
    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
1 ✔
610
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
1 ✔
611
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
1 ✔
612

613
    if (hasLength(this.typeAliasesPackage)) {
1 ✔
614
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
1 ✔
615
          .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
1 ✔
616
          .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
1 ✔
617
    }
618

619
    if (!isEmpty(this.typeAliases)) {
1 ✔
620
      Stream.of(this.typeAliases).forEach(typeAlias -> {
1 ✔
621
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
1 ✔
622
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
1 ✔
623
      });
1 ✔
624
    }
625

626
    if (!isEmpty(this.plugins)) {
1 ✔
627
      Stream.of(this.plugins).forEach(plugin -> {
1 ✔
628
        targetConfiguration.addInterceptor(plugin);
1 ✔
629
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
1 ✔
630
      });
1 ✔
631
    }
632

633
    if (hasLength(this.typeHandlersPackage)) {
1 ✔
634
      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
1 ✔
635
          .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
1 !
636
          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
1 ✔
637
    }
638

639
    if (!isEmpty(this.typeHandlers)) {
1 ✔
640
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
1 ✔
641
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
1 ✔
642
        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
1 ✔
643
      });
1 ✔
644
    }
645

646
    targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);
1 ✔
647

648
    if (!isEmpty(this.scriptingLanguageDrivers)) {
1 ✔
649
      Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
1 ✔
650
        targetConfiguration.getLanguageRegistry().register(languageDriver);
1 ✔
651
        LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
1 ✔
652
      });
1 ✔
653
    }
654
    Optional.ofNullable(this.defaultScriptingLanguageDriver)
1 ✔
655
        .ifPresent(targetConfiguration::setDefaultScriptingLanguage);
1 ✔
656

657
    if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
1 !
658
      try {
659
        targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
×
660
      } catch (SQLException e) {
×
661
        throw new IOException("Failed getting a databaseId", e);
×
662
      }
×
663
    }
664

665
    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
1 ✔
666

667
    if (xmlConfigBuilder != null) {
1 ✔
668
      try {
669
        xmlConfigBuilder.parse();
1 ✔
670
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
1 ✔
671
      } catch (Exception ex) {
×
672
        throw new IOException("Failed to parse config resource: " + this.configLocation, ex);
×
673
      } finally {
674
        ErrorContext.instance().reset();
1 ✔
675
      }
676
    }
677

678
    targetConfiguration.setEnvironment(new Environment(this.environment,
1 ✔
679
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
1 ✔
680
        this.dataSource));
681

682
    if (this.mapperLocations != null) {
1 ✔
683
      if (this.mapperLocations.length == 0) {
1 ✔
684
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
1 ✔
685
      } else {
686
        for (Resource mapperLocation : this.mapperLocations) {
1 ✔
687
          if (mapperLocation == null) {
1 ✔
688
            continue;
1 ✔
689
          }
690
          try {
691
            final InputStream inputStream;
692
            try {
693
              inputStream = mapperLocation.getInputStream();
1 ✔
694
            } catch (Exception e) {
1 ✔
695
              throw new IOException("Failed to open mapping resource: '" + mapperLocation
1 ✔
696
                  + "'. When using a resource pattern, resolve it with ResourcePatternResolver.getResources() before passing the results to setMapperLocations().",
697
                  e);
698
            }
1 ✔
699
            try {
700
              var xmlMapperBuilder = new XMLMapperBuilder(inputStream, targetConfiguration, mapperLocation.toString(),
1 ✔
701
                  targetConfiguration.getSqlFragments());
1 ✔
702
              xmlMapperBuilder.parse();
1 ✔
703
            } catch (Exception e) {
1 ✔
704
              throw new IOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
1 ✔
705
            }
1 ✔
706
          } finally {
707
            ErrorContext.instance().reset();
1 ✔
708
          }
709
          LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
1 ✔
710
        }
711
      }
712
    } else {
713
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
1 ✔
714
    }
715

716
    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
1 ✔
717
  }
718

719
  @Override
720
  public SqlSessionFactory getObject() throws Exception {
721
    if (this.sqlSessionFactory == null) {
1 ✔
722
      afterPropertiesSet();
1 ✔
723
    }
724

725
    return this.sqlSessionFactory;
1 ✔
726
  }
727

728
  @Override
729
  public Class<? extends SqlSessionFactory> getObjectType() {
730
    return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
1 ✔
731
  }
732

733
  @Override
734
  public boolean isSingleton() {
735
    return true;
1 ✔
736
  }
737

738
  @Override
739
  public void onApplicationEvent(ContextRefreshedEvent event) {
740
    if (failFast) {
1 !
741
      // fail-fast -> check all statements are completed
742
      this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
×
743
    }
744
  }
1 ✔
745

746
  private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) throws IOException {
747
    Set<Class<?>> classes = new HashSet<>();
1 ✔
748
    var packagePatternArray = tokenizeToStringArray(packagePatterns,
1 ✔
749
        ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
750
    for (String packagePattern : packagePatternArray) {
1 ✔
751
      var resources = RESOURCE_PATTERN_RESOLVER.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
1 ✔
752
          + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class");
1 ✔
753
      for (Resource resource : resources) {
1 ✔
754
        try {
755
          var classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata();
1 ✔
756
          Class<?> clazz = Resources.classForName(classMetadata.getClassName());
1 ✔
757
          if (assignableType == null || assignableType.isAssignableFrom(clazz)) {
1 ✔
758
            classes.add(clazz);
1 ✔
759
          }
760
        } catch (Throwable e) {
×
761
          LOGGER.warn(() -> "Cannot load the '" + resource + "'. Cause by " + e.toString());
×
762
        }
1 ✔
763
      }
764
    }
765
    return classes;
1 ✔
766
  }
767

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

© 2026 Coveralls, Inc