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