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