View Javadoc
1   /**
2    *    Copyright 2009-2015 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.LinkedList;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Properties;
26  import java.util.Set;
27  
28  import org.apache.ibatis.binding.MapperRegistry;
29  import org.apache.ibatis.builder.CacheRefResolver;
30  import org.apache.ibatis.builder.ResultMapResolver;
31  import org.apache.ibatis.builder.annotation.MethodResolver;
32  import org.apache.ibatis.builder.xml.XMLStatementBuilder;
33  import org.apache.ibatis.cache.Cache;
34  import org.apache.ibatis.cache.decorators.FifoCache;
35  import org.apache.ibatis.cache.decorators.LruCache;
36  import org.apache.ibatis.cache.decorators.SoftCache;
37  import org.apache.ibatis.cache.decorators.WeakCache;
38  import org.apache.ibatis.cache.impl.PerpetualCache;
39  import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
40  import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
41  import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
42  import org.apache.ibatis.executor.BatchExecutor;
43  import org.apache.ibatis.executor.CachingExecutor;
44  import org.apache.ibatis.executor.Executor;
45  import org.apache.ibatis.executor.ReuseExecutor;
46  import org.apache.ibatis.executor.SimpleExecutor;
47  import org.apache.ibatis.executor.keygen.KeyGenerator;
48  import org.apache.ibatis.executor.loader.ProxyFactory;
49  import org.apache.ibatis.executor.loader.cglib.CglibProxyFactory;
50  import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
51  import org.apache.ibatis.executor.parameter.ParameterHandler;
52  import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
53  import org.apache.ibatis.executor.resultset.ResultSetHandler;
54  import org.apache.ibatis.executor.statement.RoutingStatementHandler;
55  import org.apache.ibatis.executor.statement.StatementHandler;
56  import org.apache.ibatis.logging.Log;
57  import org.apache.ibatis.logging.LogFactory;
58  import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
59  import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
60  import org.apache.ibatis.logging.log4j.Log4jImpl;
61  import org.apache.ibatis.logging.log4j2.Log4j2Impl;
62  import org.apache.ibatis.logging.nologging.NoLoggingImpl;
63  import org.apache.ibatis.logging.slf4j.Slf4jImpl;
64  import org.apache.ibatis.logging.stdout.StdOutImpl;
65  import org.apache.ibatis.mapping.BoundSql;
66  import org.apache.ibatis.mapping.Environment;
67  import org.apache.ibatis.mapping.MappedStatement;
68  import org.apache.ibatis.mapping.ParameterMap;
69  import org.apache.ibatis.mapping.ResultMap;
70  import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
71  import org.apache.ibatis.parsing.XNode;
72  import org.apache.ibatis.plugin.Interceptor;
73  import org.apache.ibatis.plugin.InterceptorChain;
74  import org.apache.ibatis.reflection.DefaultReflectorFactory;
75  import org.apache.ibatis.reflection.MetaObject;
76  import org.apache.ibatis.reflection.ReflectorFactory;
77  import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
78  import org.apache.ibatis.reflection.factory.ObjectFactory;
79  import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
80  import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
81  import org.apache.ibatis.scripting.LanguageDriver;
82  import org.apache.ibatis.scripting.LanguageDriverRegistry;
83  import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
84  import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
85  import org.apache.ibatis.transaction.Transaction;
86  import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
87  import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
88  import org.apache.ibatis.type.JdbcType;
89  import org.apache.ibatis.type.TypeAliasRegistry;
90  import org.apache.ibatis.type.TypeHandlerRegistry;
91  
92  /**
93   * @author Clinton Begin
94   */
95  public class Configuration {
96  
97    protected Environment environment;
98  
99    protected boolean safeRowBoundsEnabled = false;
100   protected boolean safeResultHandlerEnabled = true;
101   protected boolean mapUnderscoreToCamelCase = false;
102   protected boolean aggressiveLazyLoading = true;
103   protected boolean multipleResultSetsEnabled = true;
104   protected boolean useGeneratedKeys = false;
105   protected boolean useColumnLabel = true;
106   protected boolean cacheEnabled = true;
107   protected boolean callSettersOnNulls = false;
108 
109   protected String logPrefix;
110   protected Class <? extends Log> logImpl;
111   protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
112   protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
113   protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
114   protected Integer defaultStatementTimeout;
115   protected Integer defaultFetchSize;
116   protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
117   protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
118 
119   protected Properties variables = new Properties();
120   protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
121   protected ObjectFactory objectFactory = new DefaultObjectFactory();
122   protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
123   protected MapperRegistry mapperRegistry = new MapperRegistry(this);
124 
125   protected boolean lazyLoadingEnabled = false;
126   protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
127 
128   protected String databaseId;
129   /**
130    * Configuration factory class.
131    * Used to create Configuration for loading deserialized unread properties.
132    *
133    * @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300</a> (google code)
134    */
135   protected Class<?> configurationFactory;
136 
137   protected final InterceptorChain interceptorChain = new InterceptorChain();
138   protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
139   protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
140   protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
141 
142   protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
143   protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
144   protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
145   protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
146   protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
147 
148   protected final Set<String> loadedResources = new HashSet<String>();
149   protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
150 
151   protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
152   protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
153   protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
154   protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
155 
156   /*
157    * A map holds cache-ref relationship. The key is the namespace that
158    * references a cache bound to another namespace and the value is the
159    * namespace which the actual cache is bound to.
160    */
161   protected final Map<String, String> cacheRefMap = new HashMap<String, String>();
162 
163   public Configuration(Environment environment) {
164     this();
165     this.environment = environment;
166   }
167 
168   public Configuration() {
169     typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
170     typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
171 
172     typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
173     typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
174     typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
175 
176     typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
177     typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
178     typeAliasRegistry.registerAlias("LRU", LruCache.class);
179     typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
180     typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
181 
182     typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
183 
184     typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
185     typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
186 
187     typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
188     typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
189     typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
190     typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
191     typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
192     typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
193     typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
194 
195     typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
196     typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
197 
198     languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
199     languageRegistry.register(RawLanguageDriver.class);
200   }
201 
202   public String getLogPrefix() {
203     return logPrefix;
204   }
205 
206   public void setLogPrefix(String logPrefix) {
207     this.logPrefix = logPrefix;
208   }
209 
210   public Class<? extends Log> getLogImpl() {
211     return logImpl;
212   }
213 
214   @SuppressWarnings("unchecked")
215   public void setLogImpl(Class<?> logImpl) {
216     if (logImpl != null) {
217       this.logImpl = (Class<? extends Log>) logImpl;
218       LogFactory.useCustomLogging(this.logImpl);
219     }
220   }
221 
222   public boolean isCallSettersOnNulls() {
223     return callSettersOnNulls;
224   }
225 
226   public void setCallSettersOnNulls(boolean callSettersOnNulls) {
227     this.callSettersOnNulls = callSettersOnNulls;
228   }
229 
230   public String getDatabaseId() {
231     return databaseId;
232   }
233 
234   public void setDatabaseId(String databaseId) {
235     this.databaseId = databaseId;
236   }
237 
238   public Class<?> getConfigurationFactory() {
239     return configurationFactory;
240   }
241 
242   public void setConfigurationFactory(Class<?> configurationFactory) {
243     this.configurationFactory = configurationFactory;
244   }
245 
246   public boolean isSafeResultHandlerEnabled() {
247     return safeResultHandlerEnabled;
248   }
249 
250   public void setSafeResultHandlerEnabled(boolean safeResultHandlerEnabled) {
251     this.safeResultHandlerEnabled = safeResultHandlerEnabled;
252   }
253 
254   public boolean isSafeRowBoundsEnabled() {
255     return safeRowBoundsEnabled;
256   }
257 
258   public void setSafeRowBoundsEnabled(boolean safeRowBoundsEnabled) {
259     this.safeRowBoundsEnabled = safeRowBoundsEnabled;
260   }
261 
262   public boolean isMapUnderscoreToCamelCase() {
263     return mapUnderscoreToCamelCase;
264   }
265 
266   public void setMapUnderscoreToCamelCase(boolean mapUnderscoreToCamelCase) {
267     this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase;
268   }
269 
270   public void addLoadedResource(String resource) {
271     loadedResources.add(resource);
272   }
273 
274   public boolean isResourceLoaded(String resource) {
275     return loadedResources.contains(resource);
276   }
277 
278   public Environment getEnvironment() {
279     return environment;
280   }
281 
282   public void setEnvironment(Environment environment) {
283     this.environment = environment;
284   }
285 
286   public AutoMappingBehavior getAutoMappingBehavior() {
287     return autoMappingBehavior;
288   }
289 
290   public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) {
291     this.autoMappingBehavior = autoMappingBehavior;
292   }
293 
294   public boolean isLazyLoadingEnabled() {
295     return lazyLoadingEnabled;
296   }
297 
298   public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) {
299     this.lazyLoadingEnabled = lazyLoadingEnabled;
300   }
301 
302   public ProxyFactory getProxyFactory() {
303     return proxyFactory;
304   }
305 
306   public void setProxyFactory(ProxyFactory proxyFactory) {
307     if (proxyFactory == null) {
308       proxyFactory = new JavassistProxyFactory();
309     }
310     this.proxyFactory = proxyFactory;
311   }
312 
313   public boolean isAggressiveLazyLoading() {
314     return aggressiveLazyLoading;
315   }
316 
317   public void setAggressiveLazyLoading(boolean aggressiveLazyLoading) {
318     this.aggressiveLazyLoading = aggressiveLazyLoading;
319   }
320 
321   public boolean isMultipleResultSetsEnabled() {
322     return multipleResultSetsEnabled;
323   }
324 
325   public void setMultipleResultSetsEnabled(boolean multipleResultSetsEnabled) {
326     this.multipleResultSetsEnabled = multipleResultSetsEnabled;
327   }
328 
329   public Set<String> getLazyLoadTriggerMethods() {
330     return lazyLoadTriggerMethods;
331   }
332 
333   public void setLazyLoadTriggerMethods(Set<String> lazyLoadTriggerMethods) {
334     this.lazyLoadTriggerMethods = lazyLoadTriggerMethods;
335   }
336 
337   public boolean isUseGeneratedKeys() {
338     return useGeneratedKeys;
339   }
340 
341   public void setUseGeneratedKeys(boolean useGeneratedKeys) {
342     this.useGeneratedKeys = useGeneratedKeys;
343   }
344 
345   public ExecutorType getDefaultExecutorType() {
346     return defaultExecutorType;
347   }
348 
349   public void setDefaultExecutorType(ExecutorType defaultExecutorType) {
350     this.defaultExecutorType = defaultExecutorType;
351   }
352 
353   public boolean isCacheEnabled() {
354     return cacheEnabled;
355   }
356 
357   public void setCacheEnabled(boolean cacheEnabled) {
358     this.cacheEnabled = cacheEnabled;
359   }
360 
361   public Integer getDefaultStatementTimeout() {
362     return defaultStatementTimeout;
363   }
364 
365   public void setDefaultStatementTimeout(Integer defaultStatementTimeout) {
366     this.defaultStatementTimeout = defaultStatementTimeout;
367   }
368 
369   public Integer getDefaultFetchSize() {
370     return defaultFetchSize;
371   }
372 
373   public void setDefaultFetchSize(Integer defaultFetchSize) {
374     this.defaultFetchSize = defaultFetchSize;
375   }
376 
377   public boolean isUseColumnLabel() {
378     return useColumnLabel;
379   }
380 
381   public void setUseColumnLabel(boolean useColumnLabel) {
382     this.useColumnLabel = useColumnLabel;
383   }
384 
385   public LocalCacheScope getLocalCacheScope() {
386     return localCacheScope;
387   }
388 
389   public void setLocalCacheScope(LocalCacheScope localCacheScope) {
390     this.localCacheScope = localCacheScope;
391   }
392 
393   public JdbcType getJdbcTypeForNull() {
394     return jdbcTypeForNull;
395   }
396 
397   public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) {
398     this.jdbcTypeForNull = jdbcTypeForNull;
399   }
400 
401   public Properties getVariables() {
402     return variables;
403   }
404 
405   public void setVariables(Properties variables) {
406     this.variables = variables;
407   }
408 
409   public TypeHandlerRegistry getTypeHandlerRegistry() {
410     return typeHandlerRegistry;
411   }
412 
413   public TypeAliasRegistry getTypeAliasRegistry() {
414     return typeAliasRegistry;
415   }
416 
417   /**
418    * @since 3.2.2
419    */
420   public MapperRegistry getMapperRegistry() {
421     return mapperRegistry;
422   }
423 
424   public ReflectorFactory getReflectorFactory() {
425 	  return reflectorFactory;
426   }
427 
428   public void setReflectorFactory(ReflectorFactory reflectorFactory) {
429 	  this.reflectorFactory = reflectorFactory;
430   }
431 
432   public ObjectFactory getObjectFactory() {
433     return objectFactory;
434   }
435 
436   public void setObjectFactory(ObjectFactory objectFactory) {
437     this.objectFactory = objectFactory;
438   }
439 
440   public ObjectWrapperFactory getObjectWrapperFactory() {
441     return objectWrapperFactory;
442   }
443 
444   public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
445     this.objectWrapperFactory = objectWrapperFactory;
446   }
447 
448   /**
449    * @since 3.2.2
450    */
451   public List<Interceptor> getInterceptors() {
452     return interceptorChain.getInterceptors();
453   }
454 
455   public LanguageDriverRegistry getLanguageRegistry() {
456     return languageRegistry;
457   }
458 
459   public void setDefaultScriptingLanguage(Class<?> driver) {
460     if (driver == null) {
461       driver = XMLLanguageDriver.class;
462     }
463     getLanguageRegistry().setDefaultDriverClass(driver);
464   }
465 
466   public LanguageDriver getDefaultScriptingLanuageInstance() {
467     return languageRegistry.getDefaultDriver();
468   }
469 
470   public MetaObject newMetaObject(Object object) {
471     return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
472   }
473 
474   public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
475     ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
476     parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
477     return parameterHandler;
478   }
479 
480   public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
481       ResultHandler resultHandler, BoundSql boundSql) {
482     ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
483     resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
484     return resultSetHandler;
485   }
486 
487   public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
488     StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
489     statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
490     return statementHandler;
491   }
492 
493   public Executor newExecutor(Transaction transaction) {
494     return newExecutor(transaction, defaultExecutorType);
495   }
496 
497   public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
498     executorType = executorType == null ? defaultExecutorType : executorType;
499     executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
500     Executor executor;
501     if (ExecutorType.BATCH == executorType) {
502       executor = new BatchExecutor(this, transaction);
503     } else if (ExecutorType.REUSE == executorType) {
504       executor = new ReuseExecutor(this, transaction);
505     } else {
506       executor = new SimpleExecutor(this, transaction);
507     }
508     if (cacheEnabled) {
509       executor = new CachingExecutor(executor);
510     }
511     executor = (Executor) interceptorChain.pluginAll(executor);
512     return executor;
513   }
514 
515   public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
516     keyGenerators.put(id, keyGenerator);
517   }
518 
519   public Collection<String> getKeyGeneratorNames() {
520     return keyGenerators.keySet();
521   }
522 
523   public Collection<KeyGenerator> getKeyGenerators() {
524     return keyGenerators.values();
525   }
526 
527   public KeyGenerator getKeyGenerator(String id) {
528     return keyGenerators.get(id);
529   }
530 
531   public boolean hasKeyGenerator(String id) {
532     return keyGenerators.containsKey(id);
533   }
534 
535   public void addCache(Cache cache) {
536     caches.put(cache.getId(), cache);
537   }
538 
539   public Collection<String> getCacheNames() {
540     return caches.keySet();
541   }
542 
543   public Collection<Cache> getCaches() {
544     return caches.values();
545   }
546 
547   public Cache getCache(String id) {
548     return caches.get(id);
549   }
550 
551   public boolean hasCache(String id) {
552     return caches.containsKey(id);
553   }
554 
555   public void addResultMap(ResultMap rm) {
556     resultMaps.put(rm.getId(), rm);
557     checkLocallyForDiscriminatedNestedResultMaps(rm);
558     checkGloballyForDiscriminatedNestedResultMaps(rm);
559   }
560 
561   public Collection<String> getResultMapNames() {
562     return resultMaps.keySet();
563   }
564 
565   public Collection<ResultMap> getResultMaps() {
566     return resultMaps.values();
567   }
568 
569   public ResultMap getResultMap(String id) {
570     return resultMaps.get(id);
571   }
572 
573   public boolean hasResultMap(String id) {
574     return resultMaps.containsKey(id);
575   }
576 
577   public void addParameterMap(ParameterMap pm) {
578     parameterMaps.put(pm.getId(), pm);
579   }
580 
581   public Collection<String> getParameterMapNames() {
582     return parameterMaps.keySet();
583   }
584 
585   public Collection<ParameterMap> getParameterMaps() {
586     return parameterMaps.values();
587   }
588 
589   public ParameterMap getParameterMap(String id) {
590     return parameterMaps.get(id);
591   }
592 
593   public boolean hasParameterMap(String id) {
594     return parameterMaps.containsKey(id);
595   }
596 
597   public void addMappedStatement(MappedStatement ms) {
598     mappedStatements.put(ms.getId(), ms);
599   }
600 
601   public Collection<String> getMappedStatementNames() {
602     buildAllStatements();
603     return mappedStatements.keySet();
604   }
605 
606   public Collection<MappedStatement> getMappedStatements() {
607     buildAllStatements();
608     return mappedStatements.values();
609   }
610 
611   public Collection<XMLStatementBuilder> getIncompleteStatements() {
612     return incompleteStatements;
613   }
614 
615   public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
616     incompleteStatements.add(incompleteStatement);
617   }
618 
619   public Collection<CacheRefResolver> getIncompleteCacheRefs() {
620     return incompleteCacheRefs;
621   }
622 
623   public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
624     incompleteCacheRefs.add(incompleteCacheRef);
625   }
626 
627   public Collection<ResultMapResolver> getIncompleteResultMaps() {
628     return incompleteResultMaps;
629   }
630 
631   public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
632     incompleteResultMaps.add(resultMapResolver);
633   }
634 
635   public void addIncompleteMethod(MethodResolver builder) {
636     incompleteMethods.add(builder);
637   }
638 
639   public Collection<MethodResolver> getIncompleteMethods() {
640     return incompleteMethods;
641   }
642 
643   public MappedStatement getMappedStatement(String id) {
644     return this.getMappedStatement(id, true);
645   }
646 
647   public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
648     if (validateIncompleteStatements) {
649       buildAllStatements();
650     }
651     return mappedStatements.get(id);
652   }
653 
654   public Map<String, XNode> getSqlFragments() {
655     return sqlFragments;
656   }
657 
658   public void addInterceptor(Interceptor interceptor) {
659     interceptorChain.addInterceptor(interceptor);
660   }
661 
662   public void addMappers(String packageName, Class<?> superType) {
663     mapperRegistry.addMappers(packageName, superType);
664   }
665 
666   public void addMappers(String packageName) {
667     mapperRegistry.addMappers(packageName);
668   }
669 
670   public <T> void addMapper(Class<T> type) {
671     mapperRegistry.addMapper(type);
672   }
673 
674   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
675     return mapperRegistry.getMapper(type, sqlSession);
676   }
677 
678   public boolean hasMapper(Class<?> type) {
679     return mapperRegistry.hasMapper(type);
680   }
681 
682   public boolean hasStatement(String statementName) {
683     return hasStatement(statementName, true);
684   }
685 
686   public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
687     if (validateIncompleteStatements) {
688       buildAllStatements();
689     }
690     return mappedStatements.containsKey(statementName);
691   }
692 
693   public void addCacheRef(String namespace, String referencedNamespace) {
694     cacheRefMap.put(namespace, referencedNamespace);
695   }
696 
697   /*
698    * Parses all the unprocessed statement nodes in the cache. It is recommended
699    * to call this method once all the mappers are added as it provides fail-fast
700    * statement validation.
701    */
702   protected void buildAllStatements() {
703     if (!incompleteResultMaps.isEmpty()) {
704       synchronized (incompleteResultMaps) {
705         // This always throws a BuilderException.
706         incompleteResultMaps.iterator().next().resolve();
707       }
708     }
709     if (!incompleteCacheRefs.isEmpty()) {
710       synchronized (incompleteCacheRefs) {
711         // This always throws a BuilderException.
712         incompleteCacheRefs.iterator().next().resolveCacheRef();
713       }
714     }
715     if (!incompleteStatements.isEmpty()) {
716       synchronized (incompleteStatements) {
717         // This always throws a BuilderException.
718         incompleteStatements.iterator().next().parseStatementNode();
719       }
720     }
721     if (!incompleteMethods.isEmpty()) {
722       synchronized (incompleteMethods) {
723         // This always throws a BuilderException.
724         incompleteMethods.iterator().next().resolve();
725       }
726     }
727   }
728 
729   /*
730    * Extracts namespace from fully qualified statement id.
731    *
732    * @param statementId
733    * @return namespace or null when id does not contain period.
734    */
735   protected String extractNamespace(String statementId) {
736     int lastPeriod = statementId.lastIndexOf('.');
737     return lastPeriod > 0 ? statementId.substring(0, lastPeriod) : null;
738   }
739 
740   // Slow but a one time cost. A better solution is welcome.
741   protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
742     if (rm.hasNestedResultMaps()) {
743       for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
744         Object value = entry.getValue();
745         if (value instanceof ResultMap) {
746           ResultMap entryResultMap = (ResultMap) value;
747           if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {
748             Collection<String> discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap().values();
749             if (discriminatedResultMapNames.contains(rm.getId())) {
750               entryResultMap.forceNestedResultMaps();
751             }
752           }
753         }
754       }
755     }
756   }
757 
758   // Slow but a one time cost. A better solution is welcome.
759   protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
760     if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
761       for (Map.Entry<String, String> entry : rm.getDiscriminator().getDiscriminatorMap().entrySet()) {
762         String discriminatedResultMapName = entry.getValue();
763         if (hasResultMap(discriminatedResultMapName)) {
764           ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);
765           if (discriminatedResultMap.hasNestedResultMaps()) {
766             rm.forceNestedResultMaps();
767             break;
768           }
769         }
770       }
771     }
772   }
773 
774   protected static class StrictMap<V> extends HashMap<String, V> {
775 
776     private static final long serialVersionUID = -4950446264854982944L;
777     private String name;
778 
779     public StrictMap(String name, int initialCapacity, float loadFactor) {
780       super(initialCapacity, loadFactor);
781       this.name = name;
782     }
783 
784     public StrictMap(String name, int initialCapacity) {
785       super(initialCapacity);
786       this.name = name;
787     }
788 
789     public StrictMap(String name) {
790       super();
791       this.name = name;
792     }
793 
794     public StrictMap(String name, Map<String, ? extends V> m) {
795       super(m);
796       this.name = name;
797     }
798 
799     @SuppressWarnings("unchecked")
800     public V put(String key, V value) {
801       if (containsKey(key)) {
802         throw new IllegalArgumentException(name + " already contains value for " + key);
803       }
804       if (key.contains(".")) {
805         final String shortKey = getShortName(key);
806         if (super.get(shortKey) == null) {
807           super.put(shortKey, value);
808         } else {
809           super.put(shortKey, (V) new Ambiguity(shortKey));
810         }
811       }
812       return super.put(key, value);
813     }
814 
815     public V get(Object key) {
816       V value = super.get(key);
817       if (value == null) {
818         throw new IllegalArgumentException(name + " does not contain value for " + key);
819       }
820       if (value instanceof Ambiguity) {
821         throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
822             + " (try using the full name including the namespace, or rename one of the entries)");
823       }
824       return value;
825     }
826 
827     private String getShortName(String key) {
828       final String[] keyparts = key.split("\\.");
829       return keyparts[keyparts.length - 1];
830     }
831 
832     protected static class Ambiguity {
833       private String subject;
834 
835       public Ambiguity(String subject) {
836         this.subject = subject;
837       }
838 
839       public String getSubject() {
840         return subject;
841       }
842     }
843   }
844 
845 }