View Javadoc
1   /*
2    *    Copyright 2009-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    *       http://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.apache.ibatis.session;
17  
18  import java.util.Arrays;
19  import java.util.Collection;
20  import java.util.HashMap;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Properties;
27  import java.util.Set;
28  import java.util.function.BiFunction;
29  
30  import org.apache.ibatis.binding.MapperRegistry;
31  import org.apache.ibatis.builder.CacheRefResolver;
32  import org.apache.ibatis.builder.IncompleteElementException;
33  import org.apache.ibatis.builder.ResultMapResolver;
34  import org.apache.ibatis.builder.annotation.MethodResolver;
35  import org.apache.ibatis.builder.xml.XMLStatementBuilder;
36  import org.apache.ibatis.cache.Cache;
37  import org.apache.ibatis.cache.decorators.FifoCache;
38  import org.apache.ibatis.cache.decorators.LruCache;
39  import org.apache.ibatis.cache.decorators.SoftCache;
40  import org.apache.ibatis.cache.decorators.WeakCache;
41  import org.apache.ibatis.cache.impl.PerpetualCache;
42  import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
43  import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
44  import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
45  import org.apache.ibatis.executor.BatchExecutor;
46  import org.apache.ibatis.executor.CachingExecutor;
47  import org.apache.ibatis.executor.Executor;
48  import org.apache.ibatis.executor.ReuseExecutor;
49  import org.apache.ibatis.executor.SimpleExecutor;
50  import org.apache.ibatis.executor.keygen.KeyGenerator;
51  import org.apache.ibatis.executor.loader.ProxyFactory;
52  import org.apache.ibatis.executor.loader.cglib.CglibProxyFactory;
53  import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
54  import org.apache.ibatis.executor.parameter.ParameterHandler;
55  import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
56  import org.apache.ibatis.executor.resultset.ResultSetHandler;
57  import org.apache.ibatis.executor.statement.RoutingStatementHandler;
58  import org.apache.ibatis.executor.statement.StatementHandler;
59  import org.apache.ibatis.io.VFS;
60  import org.apache.ibatis.logging.Log;
61  import org.apache.ibatis.logging.LogFactory;
62  import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
63  import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
64  import org.apache.ibatis.logging.log4j.Log4jImpl;
65  import org.apache.ibatis.logging.log4j2.Log4j2Impl;
66  import org.apache.ibatis.logging.nologging.NoLoggingImpl;
67  import org.apache.ibatis.logging.slf4j.Slf4jImpl;
68  import org.apache.ibatis.logging.stdout.StdOutImpl;
69  import org.apache.ibatis.mapping.BoundSql;
70  import org.apache.ibatis.mapping.Environment;
71  import org.apache.ibatis.mapping.MappedStatement;
72  import org.apache.ibatis.mapping.ParameterMap;
73  import org.apache.ibatis.mapping.ResultMap;
74  import org.apache.ibatis.mapping.ResultSetType;
75  import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
76  import org.apache.ibatis.parsing.XNode;
77  import org.apache.ibatis.plugin.Interceptor;
78  import org.apache.ibatis.plugin.InterceptorChain;
79  import org.apache.ibatis.reflection.DefaultReflectorFactory;
80  import org.apache.ibatis.reflection.MetaObject;
81  import org.apache.ibatis.reflection.ReflectorFactory;
82  import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
83  import org.apache.ibatis.reflection.factory.ObjectFactory;
84  import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
85  import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
86  import org.apache.ibatis.scripting.LanguageDriver;
87  import org.apache.ibatis.scripting.LanguageDriverRegistry;
88  import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
89  import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
90  import org.apache.ibatis.transaction.Transaction;
91  import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
92  import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
93  import org.apache.ibatis.type.JdbcType;
94  import org.apache.ibatis.type.TypeAliasRegistry;
95  import org.apache.ibatis.type.TypeHandler;
96  import org.apache.ibatis.type.TypeHandlerRegistry;
97  
98  /**
99   * @author Clinton Begin
100  */
101 public class Configuration {
102 
103   protected Environment environment;
104 
105   protected boolean safeRowBoundsEnabled;
106   protected boolean safeResultHandlerEnabled = true;
107   protected boolean mapUnderscoreToCamelCase;
108   protected boolean aggressiveLazyLoading;
109   protected boolean multipleResultSetsEnabled = true;
110   protected boolean useGeneratedKeys;
111   protected boolean useColumnLabel = true;
112   protected boolean cacheEnabled = true;
113   protected boolean callSettersOnNulls;
114   protected boolean useActualParamName = true;
115   protected boolean returnInstanceForEmptyRow;
116   protected boolean shrinkWhitespacesInSql;
117   protected boolean nullableOnForEach;
118   protected boolean argNameBasedConstructorAutoMapping;
119 
120   protected String logPrefix;
121   protected Class<? extends Log> logImpl;
122   protected Class<? extends VFS> vfsImpl;
123   protected Class<?> defaultSqlProviderType;
124   protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
125   protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
126   protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
127   protected Integer defaultStatementTimeout;
128   protected Integer defaultFetchSize;
129   protected ResultSetType defaultResultSetType;
130   protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
131   protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
132   protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
133 
134   protected Properties variables = new Properties();
135   protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
136   protected ObjectFactory objectFactory = new DefaultObjectFactory();
137   protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
138 
139   protected boolean lazyLoadingEnabled = false;
140   protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
141 
142   protected String databaseId;
143   /**
144    * Configuration factory class.
145    * Used to create Configuration for loading deserialized unread properties.
146    *
147    * @see <a href='https://github.com/mybatis/old-google-code-issues/issues/300'>Issue 300 (google code)</a>
148    */
149   protected Class<?> configurationFactory;
150 
151   protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
152   protected final InterceptorChain interceptorChain = new InterceptorChain();
153   protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
154   protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
155   protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
156 
157   protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
158       .conflictMessageProducer((savedValue, targetValue) ->
159           ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
160   protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
161   protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
162   protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
163   protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
164 
165   protected final Set<String> loadedResources = new HashSet<>();
166   protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
167 
168   protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
169   protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
170   protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
171   protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();
172 
173   /*
174    * A map holds cache-ref relationship. The key is the namespace that
175    * references a cache bound to another namespace and the value is the
176    * namespace which the actual cache is bound to.
177    */
178   protected final Map<String, String> cacheRefMap = new HashMap<>();
179 
180   public Configuration(Environment environment) {
181     this();
182     this.environment = environment;
183   }
184 
185   public Configuration() {
186     typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
187     typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
188 
189     typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
190     typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
191     typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
192 
193     typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
194     typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
195     typeAliasRegistry.registerAlias("LRU", LruCache.class);
196     typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
197     typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
198 
199     typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
200 
201     typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
202     typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
203 
204     typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
205     typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
206     typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
207     typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
208     typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
209     typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
210     typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
211 
212     typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
213     typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
214 
215     languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
216     languageRegistry.register(RawLanguageDriver.class);
217   }
218 
219   public String getLogPrefix() {
220     return logPrefix;
221   }
222 
223   public void setLogPrefix(String logPrefix) {
224     this.logPrefix = logPrefix;
225   }
226 
227   public Class<? extends Log> getLogImpl() {
228     return logImpl;
229   }
230 
231   public void setLogImpl(Class<? extends Log> logImpl) {
232     if (logImpl != null) {
233       this.logImpl = logImpl;
234       LogFactory.useCustomLogging(this.logImpl);
235     }
236   }
237 
238   public Class<? extends VFS> getVfsImpl() {
239     return this.vfsImpl;
240   }
241 
242   public void setVfsImpl(Class<? extends VFS> vfsImpl) {
243     if (vfsImpl != null) {
244       this.vfsImpl = vfsImpl;
245       VFS.addImplClass(this.vfsImpl);
246     }
247   }
248 
249   /**
250    * Gets an applying type when omit a type on sql provider annotation(e.g. {@link org.apache.ibatis.annotations.SelectProvider}).
251    *
252    * @return the default type for sql provider annotation
253    * @since 3.5.6
254    */
255   public Class<?> getDefaultSqlProviderType() {
256     return defaultSqlProviderType;
257   }
258 
259   /**
260    * Sets an applying type when omit a type on sql provider annotation(e.g. {@link org.apache.ibatis.annotations.SelectProvider}).
261    *
262    * @param defaultSqlProviderType
263    *          the default type for sql provider annotation
264    * @since 3.5.6
265    */
266   public void setDefaultSqlProviderType(Class<?> defaultSqlProviderType) {
267     this.defaultSqlProviderType = defaultSqlProviderType;
268   }
269 
270   public boolean isCallSettersOnNulls() {
271     return callSettersOnNulls;
272   }
273 
274   public void setCallSettersOnNulls(boolean callSettersOnNulls) {
275     this.callSettersOnNulls = callSettersOnNulls;
276   }
277 
278   public boolean isUseActualParamName() {
279     return useActualParamName;
280   }
281 
282   public void setUseActualParamName(boolean useActualParamName) {
283     this.useActualParamName = useActualParamName;
284   }
285 
286   public boolean isReturnInstanceForEmptyRow() {
287     return returnInstanceForEmptyRow;
288   }
289 
290   public void setReturnInstanceForEmptyRow(boolean returnEmptyInstance) {
291     this.returnInstanceForEmptyRow = returnEmptyInstance;
292   }
293 
294   public boolean isShrinkWhitespacesInSql() {
295     return shrinkWhitespacesInSql;
296   }
297 
298   public void setShrinkWhitespacesInSql(boolean shrinkWhitespacesInSql) {
299     this.shrinkWhitespacesInSql = shrinkWhitespacesInSql;
300   }
301 
302   /**
303    * Sets the default value of 'nullable' attribute on 'foreach' tag.
304    *
305    * @param nullableOnForEach If nullable, set to {@code true}
306    * @since 3.5.9
307    */
308   public void setNullableOnForEach(boolean nullableOnForEach) {
309     this.nullableOnForEach = nullableOnForEach;
310   }
311 
312   /**
313    * Returns the default value of 'nullable' attribute on 'foreach' tag.
314    *
315    * <p>Default is {@code false}.
316    *
317    * @return If nullable, set to {@code true}
318    * @since 3.5.9
319    */
320   public boolean isNullableOnForEach() {
321     return nullableOnForEach;
322   }
323 
324   public boolean isArgNameBasedConstructorAutoMapping() {
325     return argNameBasedConstructorAutoMapping;
326   }
327 
328   public void setArgNameBasedConstructorAutoMapping(boolean argNameBasedConstructorAutoMapping) {
329     this.argNameBasedConstructorAutoMapping = argNameBasedConstructorAutoMapping;
330   }
331 
332   public String getDatabaseId() {
333     return databaseId;
334   }
335 
336   public void setDatabaseId(String databaseId) {
337     this.databaseId = databaseId;
338   }
339 
340   public Class<?> getConfigurationFactory() {
341     return configurationFactory;
342   }
343 
344   public void setConfigurationFactory(Class<?> configurationFactory) {
345     this.configurationFactory = configurationFactory;
346   }
347 
348   public boolean isSafeResultHandlerEnabled() {
349     return safeResultHandlerEnabled;
350   }
351 
352   public void setSafeResultHandlerEnabled(boolean safeResultHandlerEnabled) {
353     this.safeResultHandlerEnabled = safeResultHandlerEnabled;
354   }
355 
356   public boolean isSafeRowBoundsEnabled() {
357     return safeRowBoundsEnabled;
358   }
359 
360   public void setSafeRowBoundsEnabled(boolean safeRowBoundsEnabled) {
361     this.safeRowBoundsEnabled = safeRowBoundsEnabled;
362   }
363 
364   public boolean isMapUnderscoreToCamelCase() {
365     return mapUnderscoreToCamelCase;
366   }
367 
368   public void setMapUnderscoreToCamelCase(boolean mapUnderscoreToCamelCase) {
369     this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase;
370   }
371 
372   public void addLoadedResource(String resource) {
373     loadedResources.add(resource);
374   }
375 
376   public boolean isResourceLoaded(String resource) {
377     return loadedResources.contains(resource);
378   }
379 
380   public Environment getEnvironment() {
381     return environment;
382   }
383 
384   public void setEnvironment(Environment environment) {
385     this.environment = environment;
386   }
387 
388   public AutoMappingBehavior getAutoMappingBehavior() {
389     return autoMappingBehavior;
390   }
391 
392   public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) {
393     this.autoMappingBehavior = autoMappingBehavior;
394   }
395 
396   /**
397    * Gets the auto mapping unknown column behavior.
398    *
399    * @return the auto mapping unknown column behavior
400    * @since 3.4.0
401    */
402   public AutoMappingUnknownColumnBehavior getAutoMappingUnknownColumnBehavior() {
403     return autoMappingUnknownColumnBehavior;
404   }
405 
406   /**
407    * Sets the auto mapping unknown column behavior.
408    *
409    * @param autoMappingUnknownColumnBehavior
410    *          the new auto mapping unknown column behavior
411    * @since 3.4.0
412    */
413   public void setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior) {
414     this.autoMappingUnknownColumnBehavior = autoMappingUnknownColumnBehavior;
415   }
416 
417   public boolean isLazyLoadingEnabled() {
418     return lazyLoadingEnabled;
419   }
420 
421   public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) {
422     this.lazyLoadingEnabled = lazyLoadingEnabled;
423   }
424 
425   public ProxyFactory getProxyFactory() {
426     return proxyFactory;
427   }
428 
429   public void setProxyFactory(ProxyFactory proxyFactory) {
430     if (proxyFactory == null) {
431       proxyFactory = new JavassistProxyFactory();
432     }
433     this.proxyFactory = proxyFactory;
434   }
435 
436   public boolean isAggressiveLazyLoading() {
437     return aggressiveLazyLoading;
438   }
439 
440   public void setAggressiveLazyLoading(boolean aggressiveLazyLoading) {
441     this.aggressiveLazyLoading = aggressiveLazyLoading;
442   }
443 
444   public boolean isMultipleResultSetsEnabled() {
445     return multipleResultSetsEnabled;
446   }
447 
448   public void setMultipleResultSetsEnabled(boolean multipleResultSetsEnabled) {
449     this.multipleResultSetsEnabled = multipleResultSetsEnabled;
450   }
451 
452   public Set<String> getLazyLoadTriggerMethods() {
453     return lazyLoadTriggerMethods;
454   }
455 
456   public void setLazyLoadTriggerMethods(Set<String> lazyLoadTriggerMethods) {
457     this.lazyLoadTriggerMethods = lazyLoadTriggerMethods;
458   }
459 
460   public boolean isUseGeneratedKeys() {
461     return useGeneratedKeys;
462   }
463 
464   public void setUseGeneratedKeys(boolean useGeneratedKeys) {
465     this.useGeneratedKeys = useGeneratedKeys;
466   }
467 
468   public ExecutorType getDefaultExecutorType() {
469     return defaultExecutorType;
470   }
471 
472   public void setDefaultExecutorType(ExecutorType defaultExecutorType) {
473     this.defaultExecutorType = defaultExecutorType;
474   }
475 
476   public boolean isCacheEnabled() {
477     return cacheEnabled;
478   }
479 
480   public void setCacheEnabled(boolean cacheEnabled) {
481     this.cacheEnabled = cacheEnabled;
482   }
483 
484   public Integer getDefaultStatementTimeout() {
485     return defaultStatementTimeout;
486   }
487 
488   public void setDefaultStatementTimeout(Integer defaultStatementTimeout) {
489     this.defaultStatementTimeout = defaultStatementTimeout;
490   }
491 
492   /**
493    * Gets the default fetch size.
494    *
495    * @return the default fetch size
496    * @since 3.3.0
497    */
498   public Integer getDefaultFetchSize() {
499     return defaultFetchSize;
500   }
501 
502   /**
503    * Sets the default fetch size.
504    *
505    * @param defaultFetchSize
506    *          the new default fetch size
507    * @since 3.3.0
508    */
509   public void setDefaultFetchSize(Integer defaultFetchSize) {
510     this.defaultFetchSize = defaultFetchSize;
511   }
512 
513   /**
514    * Gets the default result set type.
515    *
516    * @return the default result set type
517    * @since 3.5.2
518    */
519   public ResultSetType getDefaultResultSetType() {
520     return defaultResultSetType;
521   }
522 
523   /**
524    * Sets the default result set type.
525    *
526    * @param defaultResultSetType
527    *          the new default result set type
528    * @since 3.5.2
529    */
530   public void setDefaultResultSetType(ResultSetType defaultResultSetType) {
531     this.defaultResultSetType = defaultResultSetType;
532   }
533 
534   public boolean isUseColumnLabel() {
535     return useColumnLabel;
536   }
537 
538   public void setUseColumnLabel(boolean useColumnLabel) {
539     this.useColumnLabel = useColumnLabel;
540   }
541 
542   public LocalCacheScope getLocalCacheScope() {
543     return localCacheScope;
544   }
545 
546   public void setLocalCacheScope(LocalCacheScope localCacheScope) {
547     this.localCacheScope = localCacheScope;
548   }
549 
550   public JdbcType getJdbcTypeForNull() {
551     return jdbcTypeForNull;
552   }
553 
554   public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) {
555     this.jdbcTypeForNull = jdbcTypeForNull;
556   }
557 
558   public Properties getVariables() {
559     return variables;
560   }
561 
562   public void setVariables(Properties variables) {
563     this.variables = variables;
564   }
565 
566   public TypeHandlerRegistry getTypeHandlerRegistry() {
567     return typeHandlerRegistry;
568   }
569 
570   /**
571    * Set a default {@link TypeHandler} class for {@link Enum}.
572    * A default {@link TypeHandler} is {@link org.apache.ibatis.type.EnumTypeHandler}.
573    * @param typeHandler a type handler class for {@link Enum}
574    * @since 3.4.5
575    */
576   public void setDefaultEnumTypeHandler(Class<? extends TypeHandler> typeHandler) {
577     if (typeHandler != null) {
578       getTypeHandlerRegistry().setDefaultEnumTypeHandler(typeHandler);
579     }
580   }
581 
582   public TypeAliasRegistry getTypeAliasRegistry() {
583     return typeAliasRegistry;
584   }
585 
586   /**
587    * Gets the mapper registry.
588    *
589    * @return the mapper registry
590    * @since 3.2.2
591    */
592   public MapperRegistry getMapperRegistry() {
593     return mapperRegistry;
594   }
595 
596   public ReflectorFactory getReflectorFactory() {
597     return reflectorFactory;
598   }
599 
600   public void setReflectorFactory(ReflectorFactory reflectorFactory) {
601     this.reflectorFactory = reflectorFactory;
602   }
603 
604   public ObjectFactory getObjectFactory() {
605     return objectFactory;
606   }
607 
608   public void setObjectFactory(ObjectFactory objectFactory) {
609     this.objectFactory = objectFactory;
610   }
611 
612   public ObjectWrapperFactory getObjectWrapperFactory() {
613     return objectWrapperFactory;
614   }
615 
616   public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
617     this.objectWrapperFactory = objectWrapperFactory;
618   }
619 
620   /**
621    * Gets the interceptors.
622    *
623    * @return the interceptors
624    * @since 3.2.2
625    */
626   public List<Interceptor> getInterceptors() {
627     return interceptorChain.getInterceptors();
628   }
629 
630   public LanguageDriverRegistry getLanguageRegistry() {
631     return languageRegistry;
632   }
633 
634   public void setDefaultScriptingLanguage(Class<? extends LanguageDriver> driver) {
635     if (driver == null) {
636       driver = XMLLanguageDriver.class;
637     }
638     getLanguageRegistry().setDefaultDriverClass(driver);
639   }
640 
641   public LanguageDriver getDefaultScriptingLanguageInstance() {
642     return languageRegistry.getDefaultDriver();
643   }
644 
645   /**
646    * Gets the language driver.
647    *
648    * @param langClass
649    *          the lang class
650    * @return the language driver
651    * @since 3.5.1
652    */
653   public LanguageDriver getLanguageDriver(Class<? extends LanguageDriver> langClass) {
654     if (langClass == null) {
655       return languageRegistry.getDefaultDriver();
656     }
657     languageRegistry.register(langClass);
658     return languageRegistry.getDriver(langClass);
659   }
660 
661   /**
662    * Gets the default scripting language instance.
663    *
664    * @return the default scripting language instance
665    * @deprecated Use {@link #getDefaultScriptingLanguageInstance()}
666    */
667   @Deprecated
668   public LanguageDriver getDefaultScriptingLanuageInstance() {
669     return getDefaultScriptingLanguageInstance();
670   }
671 
672   public MetaObject newMetaObject(Object object) {
673     return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
674   }
675 
676   public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
677     ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
678     parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
679     return parameterHandler;
680   }
681 
682   public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
683       ResultHandler resultHandler, BoundSql boundSql) {
684     ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
685     resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
686     return resultSetHandler;
687   }
688 
689   public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
690     StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
691     statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
692     return statementHandler;
693   }
694 
695   public Executor newExecutor(Transaction transaction) {
696     return newExecutor(transaction, defaultExecutorType);
697   }
698 
699   public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
700     executorType = executorType == null ? defaultExecutorType : executorType;
701     executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
702     Executor executor;
703     if (ExecutorType.BATCH == executorType) {
704       executor = new BatchExecutor(this, transaction);
705     } else if (ExecutorType.REUSE == executorType) {
706       executor = new ReuseExecutor(this, transaction);
707     } else {
708       executor = new SimpleExecutor(this, transaction);
709     }
710     if (cacheEnabled) {
711       executor = new CachingExecutor(executor);
712     }
713     executor = (Executor) interceptorChain.pluginAll(executor);
714     return executor;
715   }
716 
717   public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
718     keyGenerators.put(id, keyGenerator);
719   }
720 
721   public Collection<String> getKeyGeneratorNames() {
722     return keyGenerators.keySet();
723   }
724 
725   public Collection<KeyGenerator> getKeyGenerators() {
726     return keyGenerators.values();
727   }
728 
729   public KeyGenerator getKeyGenerator(String id) {
730     return keyGenerators.get(id);
731   }
732 
733   public boolean hasKeyGenerator(String id) {
734     return keyGenerators.containsKey(id);
735   }
736 
737   public void addCache(Cache cache) {
738     caches.put(cache.getId(), cache);
739   }
740 
741   public Collection<String> getCacheNames() {
742     return caches.keySet();
743   }
744 
745   public Collection<Cache> getCaches() {
746     return caches.values();
747   }
748 
749   public Cache getCache(String id) {
750     return caches.get(id);
751   }
752 
753   public boolean hasCache(String id) {
754     return caches.containsKey(id);
755   }
756 
757   public void addResultMap(ResultMap rm) {
758     resultMaps.put(rm.getId(), rm);
759     checkLocallyForDiscriminatedNestedResultMaps(rm);
760     checkGloballyForDiscriminatedNestedResultMaps(rm);
761   }
762 
763   public Collection<String> getResultMapNames() {
764     return resultMaps.keySet();
765   }
766 
767   public Collection<ResultMap> getResultMaps() {
768     return resultMaps.values();
769   }
770 
771   public ResultMap getResultMap(String id) {
772     return resultMaps.get(id);
773   }
774 
775   public boolean hasResultMap(String id) {
776     return resultMaps.containsKey(id);
777   }
778 
779   public void addParameterMap(ParameterMap pm) {
780     parameterMaps.put(pm.getId(), pm);
781   }
782 
783   public Collection<String> getParameterMapNames() {
784     return parameterMaps.keySet();
785   }
786 
787   public Collection<ParameterMap> getParameterMaps() {
788     return parameterMaps.values();
789   }
790 
791   public ParameterMap getParameterMap(String id) {
792     return parameterMaps.get(id);
793   }
794 
795   public boolean hasParameterMap(String id) {
796     return parameterMaps.containsKey(id);
797   }
798 
799   public void addMappedStatement(MappedStatement ms) {
800     mappedStatements.put(ms.getId(), ms);
801   }
802 
803   public Collection<String> getMappedStatementNames() {
804     buildAllStatements();
805     return mappedStatements.keySet();
806   }
807 
808   public Collection<MappedStatement> getMappedStatements() {
809     buildAllStatements();
810     return mappedStatements.values();
811   }
812 
813   public Collection<XMLStatementBuilder> getIncompleteStatements() {
814     return incompleteStatements;
815   }
816 
817   public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
818     incompleteStatements.add(incompleteStatement);
819   }
820 
821   public Collection<CacheRefResolver> getIncompleteCacheRefs() {
822     return incompleteCacheRefs;
823   }
824 
825   public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
826     incompleteCacheRefs.add(incompleteCacheRef);
827   }
828 
829   public Collection<ResultMapResolver> getIncompleteResultMaps() {
830     return incompleteResultMaps;
831   }
832 
833   public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
834     incompleteResultMaps.add(resultMapResolver);
835   }
836 
837   public void addIncompleteMethod(MethodResolver builder) {
838     incompleteMethods.add(builder);
839   }
840 
841   public Collection<MethodResolver> getIncompleteMethods() {
842     return incompleteMethods;
843   }
844 
845   public MappedStatement getMappedStatement(String id) {
846     return this.getMappedStatement(id, true);
847   }
848 
849   public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
850     if (validateIncompleteStatements) {
851       buildAllStatements();
852     }
853     return mappedStatements.get(id);
854   }
855 
856   public Map<String, XNode> getSqlFragments() {
857     return sqlFragments;
858   }
859 
860   public void addInterceptor(Interceptor interceptor) {
861     interceptorChain.addInterceptor(interceptor);
862   }
863 
864   public void addMappers(String packageName, Class<?> superType) {
865     mapperRegistry.addMappers(packageName, superType);
866   }
867 
868   public void addMappers(String packageName) {
869     mapperRegistry.addMappers(packageName);
870   }
871 
872   public <T> void addMapper(Class<T> type) {
873     mapperRegistry.addMapper(type);
874   }
875 
876   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
877     return mapperRegistry.getMapper(type, sqlSession);
878   }
879 
880   public boolean hasMapper(Class<?> type) {
881     return mapperRegistry.hasMapper(type);
882   }
883 
884   public boolean hasStatement(String statementName) {
885     return hasStatement(statementName, true);
886   }
887 
888   public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
889     if (validateIncompleteStatements) {
890       buildAllStatements();
891     }
892     return mappedStatements.containsKey(statementName);
893   }
894 
895   public void addCacheRef(String namespace, String referencedNamespace) {
896     cacheRefMap.put(namespace, referencedNamespace);
897   }
898 
899   /*
900    * Parses all the unprocessed statement nodes in the cache. It is recommended
901    * to call this method once all the mappers are added as it provides fail-fast
902    * statement validation.
903    */
904   protected void buildAllStatements() {
905     parsePendingResultMaps();
906     if (!incompleteCacheRefs.isEmpty()) {
907       synchronized (incompleteCacheRefs) {
908         incompleteCacheRefs.removeIf(x -> x.resolveCacheRef() != null);
909       }
910     }
911     if (!incompleteStatements.isEmpty()) {
912       synchronized (incompleteStatements) {
913         incompleteStatements.removeIf(x -> {
914           x.parseStatementNode();
915           return true;
916         });
917       }
918     }
919     if (!incompleteMethods.isEmpty()) {
920       synchronized (incompleteMethods) {
921         incompleteMethods.removeIf(x -> {
922           x.resolve();
923           return true;
924         });
925       }
926     }
927   }
928 
929   private void parsePendingResultMaps() {
930     if (incompleteResultMaps.isEmpty()) {
931       return;
932     }
933     synchronized (incompleteResultMaps) {
934       boolean resolved;
935       IncompleteElementException ex = null;
936       do {
937         resolved = false;
938         Iterator<ResultMapResolver> iterator = incompleteResultMaps.iterator();
939         while (iterator.hasNext()) {
940           try {
941             iterator.next().resolve();
942             iterator.remove();
943             resolved = true;
944           } catch (IncompleteElementException e) {
945             ex = e;
946           }
947         }
948       } while (resolved);
949       if (!incompleteResultMaps.isEmpty() && ex != null) {
950         // At least one result map is unresolvable.
951         throw ex;
952       }
953     }
954   }
955 
956   /**
957    * Extracts namespace from fully qualified statement id.
958    *
959    * @param statementId
960    *          the statement id
961    * @return namespace or null when id does not contain period.
962    */
963   protected String extractNamespace(String statementId) {
964     int lastPeriod = statementId.lastIndexOf('.');
965     return lastPeriod > 0 ? statementId.substring(0, lastPeriod) : null;
966   }
967 
968   // Slow but a one time cost. A better solution is welcome.
969   protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
970     if (rm.hasNestedResultMaps()) {
971       for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
972         Object value = entry.getValue();
973         if (value instanceof ResultMap) {
974           ResultMap entryResultMap = (ResultMap) value;
975           if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {
976             Collection<String> discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap().values();
977             if (discriminatedResultMapNames.contains(rm.getId())) {
978               entryResultMap.forceNestedResultMaps();
979             }
980           }
981         }
982       }
983     }
984   }
985 
986   // Slow but a one time cost. A better solution is welcome.
987   protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
988     if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
989       for (Map.Entry<String, String> entry : rm.getDiscriminator().getDiscriminatorMap().entrySet()) {
990         String discriminatedResultMapName = entry.getValue();
991         if (hasResultMap(discriminatedResultMapName)) {
992           ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);
993           if (discriminatedResultMap.hasNestedResultMaps()) {
994             rm.forceNestedResultMaps();
995             break;
996           }
997         }
998       }
999     }
1000   }
1001 
1002   protected static class StrictMap<V> extends HashMap<String, V> {
1003 
1004     private static final long serialVersionUID = -4950446264854982944L;
1005     private final String name;
1006     private BiFunction<V, V, String> conflictMessageProducer;
1007 
1008     public StrictMap(String name, int initialCapacity, float loadFactor) {
1009       super(initialCapacity, loadFactor);
1010       this.name = name;
1011     }
1012 
1013     public StrictMap(String name, int initialCapacity) {
1014       super(initialCapacity);
1015       this.name = name;
1016     }
1017 
1018     public StrictMap(String name) {
1019       super();
1020       this.name = name;
1021     }
1022 
1023     public StrictMap(String name, Map<String, ? extends V> m) {
1024       super(m);
1025       this.name = name;
1026     }
1027 
1028     /**
1029      * Assign a function for producing a conflict error message when contains value with the same key.
1030      * <p>
1031      * function arguments are 1st is saved value and 2nd is target value.
1032      * @param conflictMessageProducer A function for producing a conflict error message
1033      * @return a conflict error message
1034      * @since 3.5.0
1035      */
1036     public StrictMap<V> conflictMessageProducer(BiFunction<V, V, String> conflictMessageProducer) {
1037       this.conflictMessageProducer = conflictMessageProducer;
1038       return this;
1039     }
1040 
1041     @Override
1042     @SuppressWarnings("unchecked")
1043     public V put(String key, V value) {
1044       if (containsKey(key)) {
1045         throw new IllegalArgumentException(name + " already contains value for " + key
1046             + (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
1047       }
1048       if (key.contains(".")) {
1049         final String shortKey = getShortName(key);
1050         if (super.get(shortKey) == null) {
1051           super.put(shortKey, value);
1052         } else {
1053           super.put(shortKey, (V) new Ambiguity(shortKey));
1054         }
1055       }
1056       return super.put(key, value);
1057     }
1058 
1059     @Override
1060     public V get(Object key) {
1061       V value = super.get(key);
1062       if (value == null) {
1063         throw new IllegalArgumentException(name + " does not contain value for " + key);
1064       }
1065       if (value instanceof Ambiguity) {
1066         throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
1067             + " (try using the full name including the namespace, or rename one of the entries)");
1068       }
1069       return value;
1070     }
1071 
1072     protected static class Ambiguity {
1073       private final String subject;
1074 
1075       public Ambiguity(String subject) {
1076         this.subject = subject;
1077       }
1078 
1079       public String getSubject() {
1080         return subject;
1081       }
1082     }
1083 
1084     private String getShortName(String key) {
1085       final String[] keyParts = key.split("\\.");
1086       return keyParts[keyParts.length - 1];
1087     }
1088   }
1089 
1090 }