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