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 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.lang.reflect.Modifier;
26 import java.sql.SQLException;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Optional;
32 import java.util.Properties;
33 import java.util.Set;
34 import java.util.function.IntFunction;
35 import java.util.stream.Stream;
36
37 import javax.sql.DataSource;
38
39 import org.apache.ibatis.builder.xml.XMLConfigBuilder;
40 import org.apache.ibatis.builder.xml.XMLMapperBuilder;
41 import org.apache.ibatis.cache.Cache;
42 import org.apache.ibatis.executor.ErrorContext;
43 import org.apache.ibatis.io.Resources;
44 import org.apache.ibatis.io.VFS;
45 import org.apache.ibatis.mapping.DatabaseIdProvider;
46 import org.apache.ibatis.mapping.Environment;
47 import org.apache.ibatis.plugin.Interceptor;
48 import org.apache.ibatis.reflection.factory.ObjectFactory;
49 import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
50 import org.apache.ibatis.scripting.LanguageDriver;
51 import org.apache.ibatis.session.Configuration;
52 import org.apache.ibatis.session.SqlSessionFactory;
53 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
54 import org.apache.ibatis.transaction.TransactionFactory;
55 import org.apache.ibatis.type.TypeHandler;
56 import org.mybatis.logging.Logger;
57 import org.mybatis.logging.LoggerFactory;
58 import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
59 import org.springframework.beans.factory.FactoryBean;
60 import org.springframework.beans.factory.InitializingBean;
61 import org.springframework.context.ApplicationListener;
62 import org.springframework.context.ConfigurableApplicationContext;
63 import org.springframework.context.event.ContextRefreshedEvent;
64 import org.springframework.core.io.Resource;
65 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
66 import org.springframework.core.io.support.ResourcePatternResolver;
67 import org.springframework.core.type.ClassMetadata;
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 public class SqlSessionFactoryBean
93 implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ContextRefreshedEvent> {
94
95 private static final Logger LOGGER = LoggerFactory.getLogger(SqlSessionFactoryBean.class);
96
97 private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();
98 private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();
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();
113
114 private SqlSessionFactory sqlSessionFactory;
115
116
117 private String environment = SqlSessionFactoryBean.class.getSimpleName();
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
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
153
154
155
156
157
158
159 public void setObjectFactory(ObjectFactory objectFactory) {
160 this.objectFactory = objectFactory;
161 }
162
163
164
165
166
167
168
169
170
171 public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
172 this.objectWrapperFactory = objectWrapperFactory;
173 }
174
175
176
177
178
179
180
181
182 public DatabaseIdProvider getDatabaseIdProvider() {
183 return databaseIdProvider;
184 }
185
186
187
188
189
190
191
192
193
194 public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
195 this.databaseIdProvider = databaseIdProvider;
196 }
197
198
199
200
201
202
203 public Class<? extends VFS> getVfs() {
204 return this.vfs;
205 }
206
207
208
209
210
211
212
213 public void setVfs(Class<? extends VFS> vfs) {
214 this.vfs = vfs;
215 }
216
217
218
219
220
221
222 public Cache getCache() {
223 return this.cache;
224 }
225
226
227
228
229
230
231
232 public void setCache(Cache cache) {
233 this.cache = cache;
234 }
235
236
237
238
239
240
241
242
243
244 public void setPlugins(Interceptor... plugins) {
245 this.plugins = plugins;
246 }
247
248
249
250
251
252
253
254
255
256
257
258 public void setTypeAliasesPackage(String typeAliasesPackage) {
259 this.typeAliasesPackage = typeAliasesPackage;
260 }
261
262
263
264
265
266
267
268
269
270
271 public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
272 this.typeAliasesSuperType = typeAliasesSuperType;
273 }
274
275
276
277
278
279
280
281
282
283
284
285 public void setTypeHandlersPackage(String typeHandlersPackage) {
286 this.typeHandlersPackage = typeHandlersPackage;
287 }
288
289
290
291
292
293
294
295
296
297 public void setTypeHandlers(TypeHandler<?>... typeHandlers) {
298 this.typeHandlers = typeHandlers;
299 }
300
301
302
303
304
305
306
307
308
309 public void setDefaultEnumTypeHandler(
310 @SuppressWarnings("rawtypes") Class<? extends TypeHandler> defaultEnumTypeHandler) {
311 this.defaultEnumTypeHandler = defaultEnumTypeHandler;
312 }
313
314
315
316
317
318
319
320
321
322 public void setTypeAliases(Class<?>... typeAliases) {
323 this.typeAliases = typeAliases;
324 }
325
326
327
328
329
330
331
332
333
334
335 public void setFailFast(boolean failFast) {
336 this.failFast = failFast;
337 }
338
339
340
341
342
343
344
345
346 public void setConfigLocation(Resource configLocation) {
347 this.configLocation = configLocation;
348 }
349
350
351
352
353
354
355
356
357
358 public void setConfiguration(Configuration configuration) {
359 this.configuration = configuration;
360 }
361
362
363
364
365
366
367
368
369
370
371
372
373 public void setMapperLocations(Resource... mapperLocations) {
374 this.mapperLocations = mapperLocations;
375 }
376
377
378
379
380
381
382
383
384
385 public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {
386 this.configurationProperties = sqlSessionFactoryProperties;
387 }
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406 public void setDataSource(DataSource dataSource) {
407 if (dataSource instanceof TransactionAwareDataSourceProxy) {
408
409
410
411
412 this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
413 } else {
414 this.dataSource = dataSource;
415 }
416 }
417
418
419
420
421
422
423
424
425
426
427 public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
428 this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
429 }
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446 public void setTransactionFactory(TransactionFactory transactionFactory) {
447 this.transactionFactory = transactionFactory;
448 }
449
450
451
452
453
454
455
456
457 public void setEnvironment(String environment) {
458 this.environment = environment;
459 }
460
461
462
463
464
465
466
467
468
469 public void setScriptingLanguageDrivers(LanguageDriver... scriptingLanguageDrivers) {
470 this.scriptingLanguageDrivers = scriptingLanguageDrivers;
471 }
472
473
474
475
476
477
478
479
480
481 public void setDefaultScriptingLanguageDriver(Class<? extends LanguageDriver> defaultScriptingLanguageDriver) {
482 this.defaultScriptingLanguageDriver = defaultScriptingLanguageDriver;
483 }
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500 public void addMapperLocations(Resource... mapperLocations) {
501 setMapperLocations(appendArrays(this.mapperLocations, mapperLocations, Resource[]::new));
502 }
503
504
505
506
507
508
509
510
511
512 public void addTypeHandlers(TypeHandler<?>... typeHandlers) {
513 setTypeHandlers(appendArrays(this.typeHandlers, typeHandlers, TypeHandler[]::new));
514 }
515
516
517
518
519
520
521
522
523
524 public void addScriptingLanguageDrivers(LanguageDriver... scriptingLanguageDrivers) {
525 setScriptingLanguageDrivers(
526 appendArrays(this.scriptingLanguageDrivers, scriptingLanguageDrivers, LanguageDriver[]::new));
527 }
528
529
530
531
532
533
534
535
536
537 public void addPlugins(Interceptor... plugins) {
538 setPlugins(appendArrays(this.plugins, plugins, Interceptor[]::new));
539 }
540
541
542
543
544
545
546
547
548
549 public void addTypeAliases(Class<?>... typeAliases) {
550 setTypeAliases(appendArrays(this.typeAliases, typeAliases, Class[]::new));
551 }
552
553 private <T> T[] appendArrays(T[] oldArrays, T[] newArrays, IntFunction<T[]> generator) {
554 if (oldArrays == null) {
555 return newArrays;
556 } else {
557 if (newArrays == null) {
558 return oldArrays;
559 } else {
560 List<T> newList = new ArrayList<>(Arrays.asList(oldArrays));
561 newList.addAll(Arrays.asList(newArrays));
562 return newList.toArray(generator.apply(0));
563 }
564 }
565 }
566
567
568
569
570 @Override
571 public void afterPropertiesSet() throws Exception {
572 notNull(dataSource, "Property 'dataSource' is required");
573 notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
574 state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
575 "Property 'configuration' and 'configLocation' can not specified with together");
576
577 this.sqlSessionFactory = buildSqlSessionFactory();
578 }
579
580
581
582
583
584
585
586
587
588
589
590
591
592 protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
593
594 final Configuration targetConfiguration;
595
596 XMLConfigBuilder xmlConfigBuilder = null;
597 if (this.configuration != null) {
598 targetConfiguration = this.configuration;
599 if (targetConfiguration.getVariables() == null) {
600 targetConfiguration.setVariables(this.configurationProperties);
601 } else if (this.configurationProperties != null) {
602 targetConfiguration.getVariables().putAll(this.configurationProperties);
603 }
604 } else if (this.configLocation != null) {
605 xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
606 targetConfiguration = xmlConfigBuilder.getConfiguration();
607 } else {
608 LOGGER.debug(
609 () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
610 targetConfiguration = new Configuration();
611 Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
612 }
613
614 Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
615 Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
616 Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
617
618 if (hasLength(this.typeAliasesPackage)) {
619 scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
620 .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
621 .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
622 }
623
624 if (!isEmpty(this.typeAliases)) {
625 Stream.of(this.typeAliases).forEach(typeAlias -> {
626 targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
627 LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
628 });
629 }
630
631 if (!isEmpty(this.plugins)) {
632 Stream.of(this.plugins).forEach(plugin -> {
633 targetConfiguration.addInterceptor(plugin);
634 LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
635 });
636 }
637
638 if (hasLength(this.typeHandlersPackage)) {
639 scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
640 .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
641 .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
642 }
643
644 if (!isEmpty(this.typeHandlers)) {
645 Stream.of(this.typeHandlers).forEach(typeHandler -> {
646 targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
647 LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
648 });
649 }
650
651 targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);
652
653 if (!isEmpty(this.scriptingLanguageDrivers)) {
654 Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
655 targetConfiguration.getLanguageRegistry().register(languageDriver);
656 LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
657 });
658 }
659 Optional.ofNullable(this.defaultScriptingLanguageDriver)
660 .ifPresent(targetConfiguration::setDefaultScriptingLanguage);
661
662 if (this.databaseIdProvider != null) {
663 try {
664 targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
665 } catch (SQLException e) {
666 throw new IOException("Failed getting a databaseId", e);
667 }
668 }
669
670 Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
671
672 if (xmlConfigBuilder != null) {
673 try {
674 xmlConfigBuilder.parse();
675 LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
676 } catch (Exception ex) {
677 throw new IOException("Failed to parse config resource: " + this.configLocation, ex);
678 } finally {
679 ErrorContext.instance().reset();
680 }
681 }
682
683 targetConfiguration.setEnvironment(new Environment(this.environment,
684 this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
685 this.dataSource));
686
687 if (this.mapperLocations != null) {
688 if (this.mapperLocations.length == 0) {
689 LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
690 } else {
691 for (Resource mapperLocation : this.mapperLocations) {
692 if (mapperLocation == null) {
693 continue;
694 }
695 try {
696 XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
697 targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
698 xmlMapperBuilder.parse();
699 } catch (Exception e) {
700 throw new IOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
701 } finally {
702 ErrorContext.instance().reset();
703 }
704 LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
705 }
706 }
707 } else {
708 LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
709 }
710
711 return this.sqlSessionFactoryBuilder.build(targetConfiguration);
712 }
713
714
715
716
717 @Override
718 public SqlSessionFactory getObject() throws Exception {
719 if (this.sqlSessionFactory == null) {
720 afterPropertiesSet();
721 }
722
723 return this.sqlSessionFactory;
724 }
725
726
727
728
729 @Override
730 public Class<? extends SqlSessionFactory> getObjectType() {
731 return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
732 }
733
734
735
736
737 @Override
738 public boolean isSingleton() {
739 return true;
740 }
741
742
743
744
745 @Override
746 public void onApplicationEvent(ContextRefreshedEvent event) {
747 if (failFast) {
748
749 this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
750 }
751 }
752
753 private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) throws IOException {
754 Set<Class<?>> classes = new HashSet<>();
755 String[] packagePatternArray = tokenizeToStringArray(packagePatterns,
756 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
757 for (String packagePattern : packagePatternArray) {
758 Resource[] resources = RESOURCE_PATTERN_RESOLVER.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
759 + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class");
760 for (Resource resource : resources) {
761 try {
762 ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata();
763 Class<?> clazz = Resources.classForName(classMetadata.getClassName());
764 if (assignableType == null || assignableType.isAssignableFrom(clazz)) {
765 classes.add(clazz);
766 }
767 } catch (Throwable e) {
768 LOGGER.warn(() -> "Cannot load the '" + resource + "'. Cause by " + e.toString());
769 }
770 }
771 }
772 return classes;
773 }
774
775 }