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