Configuration.java

  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. import java.util.Arrays;
  18. import java.util.Collection;
  19. import java.util.HashMap;
  20. import java.util.HashSet;
  21. import java.util.Iterator;
  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. import java.util.concurrent.ConcurrentHashMap;
  28. import java.util.concurrent.locks.ReentrantLock;
  29. import java.util.function.BiFunction;

  30. import org.apache.ibatis.binding.MapperRegistry;
  31. import org.apache.ibatis.builder.CacheRefResolver;
  32. import org.apache.ibatis.builder.IncompleteElementException;
  33. import org.apache.ibatis.builder.ResultMapResolver;
  34. import org.apache.ibatis.builder.annotation.MethodResolver;
  35. import org.apache.ibatis.builder.xml.XMLStatementBuilder;
  36. import org.apache.ibatis.cache.Cache;
  37. import org.apache.ibatis.cache.decorators.FifoCache;
  38. import org.apache.ibatis.cache.decorators.LruCache;
  39. import org.apache.ibatis.cache.decorators.SoftCache;
  40. import org.apache.ibatis.cache.decorators.WeakCache;
  41. import org.apache.ibatis.cache.impl.PerpetualCache;
  42. import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
  43. import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
  44. import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
  45. import org.apache.ibatis.executor.BatchExecutor;
  46. import org.apache.ibatis.executor.CachingExecutor;
  47. import org.apache.ibatis.executor.Executor;
  48. import org.apache.ibatis.executor.ReuseExecutor;
  49. import org.apache.ibatis.executor.SimpleExecutor;
  50. import org.apache.ibatis.executor.keygen.KeyGenerator;
  51. import org.apache.ibatis.executor.loader.ProxyFactory;
  52. import org.apache.ibatis.executor.loader.cglib.CglibProxyFactory;
  53. import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
  54. import org.apache.ibatis.executor.parameter.ParameterHandler;
  55. import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
  56. import org.apache.ibatis.executor.resultset.ResultSetHandler;
  57. import org.apache.ibatis.executor.statement.RoutingStatementHandler;
  58. import org.apache.ibatis.executor.statement.StatementHandler;
  59. import org.apache.ibatis.io.VFS;
  60. import org.apache.ibatis.logging.Log;
  61. import org.apache.ibatis.logging.LogFactory;
  62. import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
  63. import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
  64. import org.apache.ibatis.logging.log4j.Log4jImpl;
  65. import org.apache.ibatis.logging.log4j2.Log4j2Impl;
  66. import org.apache.ibatis.logging.nologging.NoLoggingImpl;
  67. import org.apache.ibatis.logging.slf4j.Slf4jImpl;
  68. import org.apache.ibatis.logging.stdout.StdOutImpl;
  69. import org.apache.ibatis.mapping.BoundSql;
  70. import org.apache.ibatis.mapping.Environment;
  71. import org.apache.ibatis.mapping.MappedStatement;
  72. import org.apache.ibatis.mapping.ParameterMap;
  73. import org.apache.ibatis.mapping.ResultMap;
  74. import org.apache.ibatis.mapping.ResultSetType;
  75. import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
  76. import org.apache.ibatis.parsing.XNode;
  77. import org.apache.ibatis.plugin.Interceptor;
  78. import org.apache.ibatis.plugin.InterceptorChain;
  79. import org.apache.ibatis.reflection.DefaultReflectorFactory;
  80. import org.apache.ibatis.reflection.MetaObject;
  81. import org.apache.ibatis.reflection.ReflectorFactory;
  82. import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
  83. import org.apache.ibatis.reflection.factory.ObjectFactory;
  84. import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
  85. import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
  86. import org.apache.ibatis.scripting.LanguageDriver;
  87. import org.apache.ibatis.scripting.LanguageDriverRegistry;
  88. import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
  89. import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
  90. import org.apache.ibatis.transaction.Transaction;
  91. import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
  92. import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
  93. import org.apache.ibatis.type.JdbcType;
  94. import org.apache.ibatis.type.TypeAliasRegistry;
  95. import org.apache.ibatis.type.TypeHandler;
  96. import org.apache.ibatis.type.TypeHandlerRegistry;

  97. /**
  98.  * @author Clinton Begin
  99.  */
  100. public class Configuration {

  101.   protected Environment environment;

  102.   protected boolean safeRowBoundsEnabled;
  103.   protected boolean safeResultHandlerEnabled = true;
  104.   protected boolean mapUnderscoreToCamelCase;
  105.   protected boolean aggressiveLazyLoading;
  106.   protected boolean useGeneratedKeys;
  107.   protected boolean useColumnLabel = true;
  108.   protected boolean cacheEnabled = true;
  109.   protected boolean callSettersOnNulls;
  110.   protected boolean useActualParamName = true;
  111.   protected boolean returnInstanceForEmptyRow;
  112.   protected boolean shrinkWhitespacesInSql;
  113.   protected boolean nullableOnForEach;
  114.   protected boolean argNameBasedConstructorAutoMapping;

  115.   protected String logPrefix;
  116.   protected Class<? extends Log> logImpl;
  117.   protected Class<? extends VFS> vfsImpl;
  118.   protected Class<?> defaultSqlProviderType;
  119.   protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  120.   protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  121.   protected Set<String> lazyLoadTriggerMethods = new HashSet<>(
  122.       Arrays.asList("equals", "clone", "hashCode", "toString"));
  123.   protected Integer defaultStatementTimeout;
  124.   protected Integer defaultFetchSize;
  125.   protected ResultSetType defaultResultSetType;
  126.   protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  127.   protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  128.   protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;

  129.   protected Properties variables = new Properties();
  130.   protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  131.   protected ObjectFactory objectFactory = new DefaultObjectFactory();
  132.   protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

  133.   protected boolean lazyLoadingEnabled;
  134.   protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

  135.   protected String databaseId;
  136.   /**
  137.    * Configuration factory class. Used to create Configuration for loading deserialized unread properties.
  138.    *
  139.    * @see <a href='https://github.com/mybatis/old-google-code-issues/issues/300'>Issue 300 (google code)</a>
  140.    */
  141.   protected Class<?> configurationFactory;

  142.   protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
  143.   protected final InterceptorChain interceptorChain = new InterceptorChain();
  144.   protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
  145.   protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  146.   protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  147.   protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>(
  148.       "Mapped Statements collection")
  149.           .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and "
  150.               + targetValue.getResource());
  151.   protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
  152.   protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
  153.   protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
  154.   protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");

  155.   protected final Set<String> loadedResources = new HashSet<>();
  156.   protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
  157.   protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
  158.   protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
  159.   protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
  160.   protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();

  161.   private final ReentrantLock incompleteResultMapsLock = new ReentrantLock();
  162.   private final ReentrantLock incompleteCacheRefsLock = new ReentrantLock();
  163.   private final ReentrantLock incompleteStatementsLock = new ReentrantLock();
  164.   private final ReentrantLock incompleteMethodsLock = new ReentrantLock();

  165.   /*
  166.    * A map holds cache-ref relationship. The key is the namespace that references a cache bound to another namespace and
  167.    * the value is the namespace which the actual cache is bound to.
  168.    */
  169.   protected final Map<String, String> cacheRefMap = new HashMap<>();

  170.   public Configuration(Environment environment) {
  171.     this();
  172.     this.environment = environment;
  173.   }

  174.   public Configuration() {
  175.     typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
  176.     typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

  177.     typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  178.     typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  179.     typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

  180.     typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
  181.     typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
  182.     typeAliasRegistry.registerAlias("LRU", LruCache.class);
  183.     typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
  184.     typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

  185.     typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

  186.     typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
  187.     typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

  188.     typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
  189.     typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
  190.     typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
  191.     typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
  192.     typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
  193.     typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
  194.     typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

  195.     typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
  196.     typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

  197.     languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
  198.     languageRegistry.register(RawLanguageDriver.class);
  199.   }

  200.   public String getLogPrefix() {
  201.     return logPrefix;
  202.   }

  203.   public void setLogPrefix(String logPrefix) {
  204.     this.logPrefix = logPrefix;
  205.   }

  206.   public Class<? extends Log> getLogImpl() {
  207.     return logImpl;
  208.   }

  209.   public void setLogImpl(Class<? extends Log> logImpl) {
  210.     if (logImpl != null) {
  211.       this.logImpl = logImpl;
  212.       LogFactory.useCustomLogging(this.logImpl);
  213.     }
  214.   }

  215.   public Class<? extends VFS> getVfsImpl() {
  216.     return this.vfsImpl;
  217.   }

  218.   public void setVfsImpl(Class<? extends VFS> vfsImpl) {
  219.     if (vfsImpl != null) {
  220.       this.vfsImpl = vfsImpl;
  221.       VFS.addImplClass(this.vfsImpl);
  222.     }
  223.   }

  224.   /**
  225.    * Gets an applying type when omit a type on sql provider annotation(e.g.
  226.    * {@link org.apache.ibatis.annotations.SelectProvider}).
  227.    *
  228.    * @return the default type for sql provider annotation
  229.    *
  230.    * @since 3.5.6
  231.    */
  232.   public Class<?> getDefaultSqlProviderType() {
  233.     return defaultSqlProviderType;
  234.   }

  235.   /**
  236.    * Sets an applying type when omit a type on sql provider annotation(e.g.
  237.    * {@link org.apache.ibatis.annotations.SelectProvider}).
  238.    *
  239.    * @param defaultSqlProviderType
  240.    *          the default type for sql provider annotation
  241.    *
  242.    * @since 3.5.6
  243.    */
  244.   public void setDefaultSqlProviderType(Class<?> defaultSqlProviderType) {
  245.     this.defaultSqlProviderType = defaultSqlProviderType;
  246.   }

  247.   public boolean isCallSettersOnNulls() {
  248.     return callSettersOnNulls;
  249.   }

  250.   public void setCallSettersOnNulls(boolean callSettersOnNulls) {
  251.     this.callSettersOnNulls = callSettersOnNulls;
  252.   }

  253.   public boolean isUseActualParamName() {
  254.     return useActualParamName;
  255.   }

  256.   public void setUseActualParamName(boolean useActualParamName) {
  257.     this.useActualParamName = useActualParamName;
  258.   }

  259.   public boolean isReturnInstanceForEmptyRow() {
  260.     return returnInstanceForEmptyRow;
  261.   }

  262.   public void setReturnInstanceForEmptyRow(boolean returnEmptyInstance) {
  263.     this.returnInstanceForEmptyRow = returnEmptyInstance;
  264.   }

  265.   public boolean isShrinkWhitespacesInSql() {
  266.     return shrinkWhitespacesInSql;
  267.   }

  268.   public void setShrinkWhitespacesInSql(boolean shrinkWhitespacesInSql) {
  269.     this.shrinkWhitespacesInSql = shrinkWhitespacesInSql;
  270.   }

  271.   /**
  272.    * Sets the default value of 'nullable' attribute on 'foreach' tag.
  273.    *
  274.    * @param nullableOnForEach
  275.    *          If nullable, set to {@code true}
  276.    *
  277.    * @since 3.5.9
  278.    */
  279.   public void setNullableOnForEach(boolean nullableOnForEach) {
  280.     this.nullableOnForEach = nullableOnForEach;
  281.   }

  282.   /**
  283.    * Returns the default value of 'nullable' attribute on 'foreach' tag.
  284.    * <p>
  285.    * Default is {@code false}.
  286.    *
  287.    * @return If nullable, set to {@code true}
  288.    *
  289.    * @since 3.5.9
  290.    */
  291.   public boolean isNullableOnForEach() {
  292.     return nullableOnForEach;
  293.   }

  294.   public boolean isArgNameBasedConstructorAutoMapping() {
  295.     return argNameBasedConstructorAutoMapping;
  296.   }

  297.   public void setArgNameBasedConstructorAutoMapping(boolean argNameBasedConstructorAutoMapping) {
  298.     this.argNameBasedConstructorAutoMapping = argNameBasedConstructorAutoMapping;
  299.   }

  300.   public String getDatabaseId() {
  301.     return databaseId;
  302.   }

  303.   public void setDatabaseId(String databaseId) {
  304.     this.databaseId = databaseId;
  305.   }

  306.   public Class<?> getConfigurationFactory() {
  307.     return configurationFactory;
  308.   }

  309.   public void setConfigurationFactory(Class<?> configurationFactory) {
  310.     this.configurationFactory = configurationFactory;
  311.   }

  312.   public boolean isSafeResultHandlerEnabled() {
  313.     return safeResultHandlerEnabled;
  314.   }

  315.   public void setSafeResultHandlerEnabled(boolean safeResultHandlerEnabled) {
  316.     this.safeResultHandlerEnabled = safeResultHandlerEnabled;
  317.   }

  318.   public boolean isSafeRowBoundsEnabled() {
  319.     return safeRowBoundsEnabled;
  320.   }

  321.   public void setSafeRowBoundsEnabled(boolean safeRowBoundsEnabled) {
  322.     this.safeRowBoundsEnabled = safeRowBoundsEnabled;
  323.   }

  324.   public boolean isMapUnderscoreToCamelCase() {
  325.     return mapUnderscoreToCamelCase;
  326.   }

  327.   public void setMapUnderscoreToCamelCase(boolean mapUnderscoreToCamelCase) {
  328.     this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase;
  329.   }

  330.   public void addLoadedResource(String resource) {
  331.     loadedResources.add(resource);
  332.   }

  333.   public boolean isResourceLoaded(String resource) {
  334.     return loadedResources.contains(resource);
  335.   }

  336.   public Environment getEnvironment() {
  337.     return environment;
  338.   }

  339.   public void setEnvironment(Environment environment) {
  340.     this.environment = environment;
  341.   }

  342.   public AutoMappingBehavior getAutoMappingBehavior() {
  343.     return autoMappingBehavior;
  344.   }

  345.   public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) {
  346.     this.autoMappingBehavior = autoMappingBehavior;
  347.   }

  348.   /**
  349.    * Gets the auto mapping unknown column behavior.
  350.    *
  351.    * @return the auto mapping unknown column behavior
  352.    *
  353.    * @since 3.4.0
  354.    */
  355.   public AutoMappingUnknownColumnBehavior getAutoMappingUnknownColumnBehavior() {
  356.     return autoMappingUnknownColumnBehavior;
  357.   }

  358.   /**
  359.    * Sets the auto mapping unknown column behavior.
  360.    *
  361.    * @param autoMappingUnknownColumnBehavior
  362.    *          the new auto mapping unknown column behavior
  363.    *
  364.    * @since 3.4.0
  365.    */
  366.   public void setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior) {
  367.     this.autoMappingUnknownColumnBehavior = autoMappingUnknownColumnBehavior;
  368.   }

  369.   public boolean isLazyLoadingEnabled() {
  370.     return lazyLoadingEnabled;
  371.   }

  372.   public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) {
  373.     this.lazyLoadingEnabled = lazyLoadingEnabled;
  374.   }

  375.   public ProxyFactory getProxyFactory() {
  376.     return proxyFactory;
  377.   }

  378.   public void setProxyFactory(ProxyFactory proxyFactory) {
  379.     if (proxyFactory == null) {
  380.       proxyFactory = new JavassistProxyFactory();
  381.     }
  382.     this.proxyFactory = proxyFactory;
  383.   }

  384.   public boolean isAggressiveLazyLoading() {
  385.     return aggressiveLazyLoading;
  386.   }

  387.   public void setAggressiveLazyLoading(boolean aggressiveLazyLoading) {
  388.     this.aggressiveLazyLoading = aggressiveLazyLoading;
  389.   }

  390.   /**
  391.    * @deprecated You can safely remove the call to this method as this option had no effect.
  392.    */
  393.   @Deprecated
  394.   public boolean isMultipleResultSetsEnabled() {
  395.     return true;
  396.   }

  397.   /**
  398.    * @deprecated You can safely remove the call to this method as this option had no effect.
  399.    */
  400.   @Deprecated
  401.   public void setMultipleResultSetsEnabled(boolean multipleResultSetsEnabled) {
  402.     // nop
  403.   }

  404.   public Set<String> getLazyLoadTriggerMethods() {
  405.     return lazyLoadTriggerMethods;
  406.   }

  407.   public void setLazyLoadTriggerMethods(Set<String> lazyLoadTriggerMethods) {
  408.     this.lazyLoadTriggerMethods = lazyLoadTriggerMethods;
  409.   }

  410.   public boolean isUseGeneratedKeys() {
  411.     return useGeneratedKeys;
  412.   }

  413.   public void setUseGeneratedKeys(boolean useGeneratedKeys) {
  414.     this.useGeneratedKeys = useGeneratedKeys;
  415.   }

  416.   public ExecutorType getDefaultExecutorType() {
  417.     return defaultExecutorType;
  418.   }

  419.   public void setDefaultExecutorType(ExecutorType defaultExecutorType) {
  420.     this.defaultExecutorType = defaultExecutorType;
  421.   }

  422.   public boolean isCacheEnabled() {
  423.     return cacheEnabled;
  424.   }

  425.   public void setCacheEnabled(boolean cacheEnabled) {
  426.     this.cacheEnabled = cacheEnabled;
  427.   }

  428.   public Integer getDefaultStatementTimeout() {
  429.     return defaultStatementTimeout;
  430.   }

  431.   public void setDefaultStatementTimeout(Integer defaultStatementTimeout) {
  432.     this.defaultStatementTimeout = defaultStatementTimeout;
  433.   }

  434.   /**
  435.    * Gets the default fetch size.
  436.    *
  437.    * @return the default fetch size
  438.    *
  439.    * @since 3.3.0
  440.    */
  441.   public Integer getDefaultFetchSize() {
  442.     return defaultFetchSize;
  443.   }

  444.   /**
  445.    * Sets the default fetch size.
  446.    *
  447.    * @param defaultFetchSize
  448.    *          the new default fetch size
  449.    *
  450.    * @since 3.3.0
  451.    */
  452.   public void setDefaultFetchSize(Integer defaultFetchSize) {
  453.     this.defaultFetchSize = defaultFetchSize;
  454.   }

  455.   /**
  456.    * Gets the default result set type.
  457.    *
  458.    * @return the default result set type
  459.    *
  460.    * @since 3.5.2
  461.    */
  462.   public ResultSetType getDefaultResultSetType() {
  463.     return defaultResultSetType;
  464.   }

  465.   /**
  466.    * Sets the default result set type.
  467.    *
  468.    * @param defaultResultSetType
  469.    *          the new default result set type
  470.    *
  471.    * @since 3.5.2
  472.    */
  473.   public void setDefaultResultSetType(ResultSetType defaultResultSetType) {
  474.     this.defaultResultSetType = defaultResultSetType;
  475.   }

  476.   public boolean isUseColumnLabel() {
  477.     return useColumnLabel;
  478.   }

  479.   public void setUseColumnLabel(boolean useColumnLabel) {
  480.     this.useColumnLabel = useColumnLabel;
  481.   }

  482.   public LocalCacheScope getLocalCacheScope() {
  483.     return localCacheScope;
  484.   }

  485.   public void setLocalCacheScope(LocalCacheScope localCacheScope) {
  486.     this.localCacheScope = localCacheScope;
  487.   }

  488.   public JdbcType getJdbcTypeForNull() {
  489.     return jdbcTypeForNull;
  490.   }

  491.   public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) {
  492.     this.jdbcTypeForNull = jdbcTypeForNull;
  493.   }

  494.   public Properties getVariables() {
  495.     return variables;
  496.   }

  497.   public void setVariables(Properties variables) {
  498.     this.variables = variables;
  499.   }

  500.   public TypeHandlerRegistry getTypeHandlerRegistry() {
  501.     return typeHandlerRegistry;
  502.   }

  503.   /**
  504.    * Set a default {@link TypeHandler} class for {@link Enum}. A default {@link TypeHandler} is
  505.    * {@link org.apache.ibatis.type.EnumTypeHandler}.
  506.    *
  507.    * @param typeHandler
  508.    *          a type handler class for {@link Enum}
  509.    *
  510.    * @since 3.4.5
  511.    */
  512.   public void setDefaultEnumTypeHandler(Class<? extends TypeHandler> typeHandler) {
  513.     if (typeHandler != null) {
  514.       getTypeHandlerRegistry().setDefaultEnumTypeHandler(typeHandler);
  515.     }
  516.   }

  517.   public TypeAliasRegistry getTypeAliasRegistry() {
  518.     return typeAliasRegistry;
  519.   }

  520.   /**
  521.    * Gets the mapper registry.
  522.    *
  523.    * @return the mapper registry
  524.    *
  525.    * @since 3.2.2
  526.    */
  527.   public MapperRegistry getMapperRegistry() {
  528.     return mapperRegistry;
  529.   }

  530.   public ReflectorFactory getReflectorFactory() {
  531.     return reflectorFactory;
  532.   }

  533.   public void setReflectorFactory(ReflectorFactory reflectorFactory) {
  534.     this.reflectorFactory = reflectorFactory;
  535.   }

  536.   public ObjectFactory getObjectFactory() {
  537.     return objectFactory;
  538.   }

  539.   public void setObjectFactory(ObjectFactory objectFactory) {
  540.     this.objectFactory = objectFactory;
  541.   }

  542.   public ObjectWrapperFactory getObjectWrapperFactory() {
  543.     return objectWrapperFactory;
  544.   }

  545.   public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
  546.     this.objectWrapperFactory = objectWrapperFactory;
  547.   }

  548.   /**
  549.    * Gets the interceptors.
  550.    *
  551.    * @return the interceptors
  552.    *
  553.    * @since 3.2.2
  554.    */
  555.   public List<Interceptor> getInterceptors() {
  556.     return interceptorChain.getInterceptors();
  557.   }

  558.   public LanguageDriverRegistry getLanguageRegistry() {
  559.     return languageRegistry;
  560.   }

  561.   public void setDefaultScriptingLanguage(Class<? extends LanguageDriver> driver) {
  562.     if (driver == null) {
  563.       driver = XMLLanguageDriver.class;
  564.     }
  565.     getLanguageRegistry().setDefaultDriverClass(driver);
  566.   }

  567.   public LanguageDriver getDefaultScriptingLanguageInstance() {
  568.     return languageRegistry.getDefaultDriver();
  569.   }

  570.   /**
  571.    * Gets the language driver.
  572.    *
  573.    * @param langClass
  574.    *          the lang class
  575.    *
  576.    * @return the language driver
  577.    *
  578.    * @since 3.5.1
  579.    */
  580.   public LanguageDriver getLanguageDriver(Class<? extends LanguageDriver> langClass) {
  581.     if (langClass == null) {
  582.       return languageRegistry.getDefaultDriver();
  583.     }
  584.     languageRegistry.register(langClass);
  585.     return languageRegistry.getDriver(langClass);
  586.   }

  587.   /**
  588.    * Gets the default scripting language instance.
  589.    *
  590.    * @return the default scripting language instance
  591.    *
  592.    * @deprecated Use {@link #getDefaultScriptingLanguageInstance()}
  593.    */
  594.   @Deprecated
  595.   public LanguageDriver getDefaultScriptingLanuageInstance() {
  596.     return getDefaultScriptingLanguageInstance();
  597.   }

  598.   public MetaObject newMetaObject(Object object) {
  599.     return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
  600.   }

  601.   public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject,
  602.       BoundSql boundSql) {
  603.     ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,
  604.         parameterObject, boundSql);
  605.     return (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  606.   }

  607.   public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds,
  608.       ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
  609.     ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler,
  610.         resultHandler, boundSql, rowBounds);
  611.     return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
  612.   }

  613.   public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,
  614.       Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  615.     StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject,
  616.         rowBounds, resultHandler, boundSql);
  617.     return (StatementHandler) interceptorChain.pluginAll(statementHandler);
  618.   }

  619.   public Executor newExecutor(Transaction transaction) {
  620.     return newExecutor(transaction, defaultExecutorType);
  621.   }

  622.   public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  623.     executorType = executorType == null ? defaultExecutorType : executorType;
  624.     Executor executor;
  625.     if (ExecutorType.BATCH == executorType) {
  626.       executor = new BatchExecutor(this, transaction);
  627.     } else if (ExecutorType.REUSE == executorType) {
  628.       executor = new ReuseExecutor(this, transaction);
  629.     } else {
  630.       executor = new SimpleExecutor(this, transaction);
  631.     }
  632.     if (cacheEnabled) {
  633.       executor = new CachingExecutor(executor);
  634.     }
  635.     return (Executor) interceptorChain.pluginAll(executor);
  636.   }

  637.   public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
  638.     keyGenerators.put(id, keyGenerator);
  639.   }

  640.   public Collection<String> getKeyGeneratorNames() {
  641.     return keyGenerators.keySet();
  642.   }

  643.   public Collection<KeyGenerator> getKeyGenerators() {
  644.     return keyGenerators.values();
  645.   }

  646.   public KeyGenerator getKeyGenerator(String id) {
  647.     return keyGenerators.get(id);
  648.   }

  649.   public boolean hasKeyGenerator(String id) {
  650.     return keyGenerators.containsKey(id);
  651.   }

  652.   public void addCache(Cache cache) {
  653.     caches.put(cache.getId(), cache);
  654.   }

  655.   public Collection<String> getCacheNames() {
  656.     return caches.keySet();
  657.   }

  658.   public Collection<Cache> getCaches() {
  659.     return caches.values();
  660.   }

  661.   public Cache getCache(String id) {
  662.     return caches.get(id);
  663.   }

  664.   public boolean hasCache(String id) {
  665.     return caches.containsKey(id);
  666.   }

  667.   public void addResultMap(ResultMap rm) {
  668.     resultMaps.put(rm.getId(), rm);
  669.     checkLocallyForDiscriminatedNestedResultMaps(rm);
  670.     checkGloballyForDiscriminatedNestedResultMaps(rm);
  671.   }

  672.   public Collection<String> getResultMapNames() {
  673.     return resultMaps.keySet();
  674.   }

  675.   public Collection<ResultMap> getResultMaps() {
  676.     return resultMaps.values();
  677.   }

  678.   public ResultMap getResultMap(String id) {
  679.     return resultMaps.get(id);
  680.   }

  681.   public boolean hasResultMap(String id) {
  682.     return resultMaps.containsKey(id);
  683.   }

  684.   public void addParameterMap(ParameterMap pm) {
  685.     parameterMaps.put(pm.getId(), pm);
  686.   }

  687.   public Collection<String> getParameterMapNames() {
  688.     return parameterMaps.keySet();
  689.   }

  690.   public Collection<ParameterMap> getParameterMaps() {
  691.     return parameterMaps.values();
  692.   }

  693.   public ParameterMap getParameterMap(String id) {
  694.     return parameterMaps.get(id);
  695.   }

  696.   public boolean hasParameterMap(String id) {
  697.     return parameterMaps.containsKey(id);
  698.   }

  699.   public void addMappedStatement(MappedStatement ms) {
  700.     mappedStatements.put(ms.getId(), ms);
  701.   }

  702.   public Collection<String> getMappedStatementNames() {
  703.     buildAllStatements();
  704.     return mappedStatements.keySet();
  705.   }

  706.   public Collection<MappedStatement> getMappedStatements() {
  707.     buildAllStatements();
  708.     return mappedStatements.values();
  709.   }

  710.   /**
  711.    * @deprecated call {@link #parsePendingStatements(boolean)}
  712.    */
  713.   @Deprecated
  714.   public Collection<XMLStatementBuilder> getIncompleteStatements() {
  715.     return incompleteStatements;
  716.   }

  717.   public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
  718.     incompleteStatementsLock.lock();
  719.     try {
  720.       incompleteStatements.add(incompleteStatement);
  721.     } finally {
  722.       incompleteStatementsLock.unlock();
  723.     }
  724.   }

  725.   /**
  726.    * @deprecated call {@link #parsePendingCacheRefs(boolean)}
  727.    */
  728.   @Deprecated
  729.   public Collection<CacheRefResolver> getIncompleteCacheRefs() {
  730.     return incompleteCacheRefs;
  731.   }

  732.   public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
  733.     incompleteCacheRefsLock.lock();
  734.     try {
  735.       incompleteCacheRefs.add(incompleteCacheRef);
  736.     } finally {
  737.       incompleteCacheRefsLock.unlock();
  738.     }
  739.   }

  740.   /**
  741.    * @deprecated call {@link #parsePendingResultMaps(boolean)}
  742.    */
  743.   @Deprecated
  744.   public Collection<ResultMapResolver> getIncompleteResultMaps() {
  745.     return incompleteResultMaps;
  746.   }

  747.   public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
  748.     incompleteResultMapsLock.lock();
  749.     try {
  750.       incompleteResultMaps.add(resultMapResolver);
  751.     } finally {
  752.       incompleteResultMapsLock.unlock();
  753.     }
  754.   }

  755.   public void addIncompleteMethod(MethodResolver builder) {
  756.     incompleteMethodsLock.lock();
  757.     try {
  758.       incompleteMethods.add(builder);
  759.     } finally {
  760.       incompleteMethodsLock.unlock();
  761.     }
  762.   }

  763.   /**
  764.    * @deprecated call {@link #parsePendingMethods(boolean)}
  765.    */
  766.   @Deprecated
  767.   public Collection<MethodResolver> getIncompleteMethods() {
  768.     return incompleteMethods;
  769.   }

  770.   public MappedStatement getMappedStatement(String id) {
  771.     return this.getMappedStatement(id, true);
  772.   }

  773.   public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
  774.     if (validateIncompleteStatements) {
  775.       buildAllStatements();
  776.     }
  777.     return mappedStatements.get(id);
  778.   }

  779.   public Map<String, XNode> getSqlFragments() {
  780.     return sqlFragments;
  781.   }

  782.   public void addInterceptor(Interceptor interceptor) {
  783.     interceptorChain.addInterceptor(interceptor);
  784.   }

  785.   public void addMappers(String packageName, Class<?> superType) {
  786.     mapperRegistry.addMappers(packageName, superType);
  787.   }

  788.   public void addMappers(String packageName) {
  789.     mapperRegistry.addMappers(packageName);
  790.   }

  791.   public <T> void addMapper(Class<T> type) {
  792.     mapperRegistry.addMapper(type);
  793.   }

  794.   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  795.     return mapperRegistry.getMapper(type, sqlSession);
  796.   }

  797.   public boolean hasMapper(Class<?> type) {
  798.     return mapperRegistry.hasMapper(type);
  799.   }

  800.   public boolean hasStatement(String statementName) {
  801.     return hasStatement(statementName, true);
  802.   }

  803.   public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
  804.     if (validateIncompleteStatements) {
  805.       buildAllStatements();
  806.     }
  807.     return mappedStatements.containsKey(statementName);
  808.   }

  809.   public void addCacheRef(String namespace, String referencedNamespace) {
  810.     cacheRefMap.put(namespace, referencedNamespace);
  811.   }

  812.   /*
  813.    * Parses all the unprocessed statement nodes in the cache. It is recommended to call this method once all the mappers
  814.    * are added as it provides fail-fast statement validation.
  815.    */
  816.   protected void buildAllStatements() {
  817.     parsePendingResultMaps(true);
  818.     parsePendingCacheRefs(true);
  819.     parsePendingStatements(true);
  820.     parsePendingMethods(true);
  821.   }

  822.   public void parsePendingMethods(boolean reportUnresolved) {
  823.     if (incompleteMethods.isEmpty()) {
  824.       return;
  825.     }
  826.     incompleteMethodsLock.lock();
  827.     try {
  828.       incompleteMethods.removeIf(x -> {
  829.         x.resolve();
  830.         return true;
  831.       });
  832.     } catch (IncompleteElementException e) {
  833.       if (reportUnresolved) {
  834.         throw e;
  835.       }
  836.     } finally {
  837.       incompleteMethodsLock.unlock();
  838.     }
  839.   }

  840.   public void parsePendingStatements(boolean reportUnresolved) {
  841.     if (incompleteStatements.isEmpty()) {
  842.       return;
  843.     }
  844.     incompleteStatementsLock.lock();
  845.     try {
  846.       incompleteStatements.removeIf(x -> {
  847.         x.parseStatementNode();
  848.         return true;
  849.       });
  850.     } catch (IncompleteElementException e) {
  851.       if (reportUnresolved) {
  852.         throw e;
  853.       }
  854.     } finally {
  855.       incompleteStatementsLock.unlock();
  856.     }
  857.   }

  858.   public void parsePendingCacheRefs(boolean reportUnresolved) {
  859.     if (incompleteCacheRefs.isEmpty()) {
  860.       return;
  861.     }
  862.     incompleteCacheRefsLock.lock();
  863.     try {
  864.       incompleteCacheRefs.removeIf(x -> x.resolveCacheRef() != null);
  865.     } catch (IncompleteElementException e) {
  866.       if (reportUnresolved) {
  867.         throw e;
  868.       }
  869.     } finally {
  870.       incompleteCacheRefsLock.unlock();
  871.     }
  872.   }

  873.   public void parsePendingResultMaps(boolean reportUnresolved) {
  874.     if (incompleteResultMaps.isEmpty()) {
  875.       return;
  876.     }
  877.     incompleteResultMapsLock.lock();
  878.     try {
  879.       boolean resolved;
  880.       IncompleteElementException ex = null;
  881.       do {
  882.         resolved = false;
  883.         Iterator<ResultMapResolver> iterator = incompleteResultMaps.iterator();
  884.         while (iterator.hasNext()) {
  885.           try {
  886.             iterator.next().resolve();
  887.             iterator.remove();
  888.             resolved = true;
  889.           } catch (IncompleteElementException e) {
  890.             ex = e;
  891.           }
  892.         }
  893.       } while (resolved);
  894.       if (reportUnresolved && !incompleteResultMaps.isEmpty() && ex != null) {
  895.         // At least one result map is unresolvable.
  896.         throw ex;
  897.       }
  898.     } finally {
  899.       incompleteResultMapsLock.unlock();
  900.     }
  901.   }

  902.   /**
  903.    * Extracts namespace from fully qualified statement id.
  904.    *
  905.    * @param statementId
  906.    *          the statement id
  907.    *
  908.    * @return namespace or null when id does not contain period.
  909.    */
  910.   protected String extractNamespace(String statementId) {
  911.     int lastPeriod = statementId.lastIndexOf('.');
  912.     return lastPeriod > 0 ? statementId.substring(0, lastPeriod) : null;
  913.   }

  914.   // Slow but a one time cost. A better solution is welcome.
  915.   protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
  916.     if (rm.hasNestedResultMaps()) {
  917.       final String resultMapId = rm.getId();
  918.       for (Object resultMapObject : resultMaps.values()) {
  919.         if (resultMapObject instanceof ResultMap) {
  920.           ResultMap entryResultMap = (ResultMap) resultMapObject;
  921.           if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {
  922.             Collection<String> discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap()
  923.                 .values();
  924.             if (discriminatedResultMapNames.contains(resultMapId)) {
  925.               entryResultMap.forceNestedResultMaps();
  926.             }
  927.           }
  928.         }
  929.       }
  930.     }
  931.   }

  932.   // Slow but a one time cost. A better solution is welcome.
  933.   protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
  934.     if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
  935.       for (String discriminatedResultMapName : rm.getDiscriminator().getDiscriminatorMap().values()) {
  936.         if (hasResultMap(discriminatedResultMapName)) {
  937.           ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);
  938.           if (discriminatedResultMap.hasNestedResultMaps()) {
  939.             rm.forceNestedResultMaps();
  940.             break;
  941.           }
  942.         }
  943.       }
  944.     }
  945.   }

  946.   protected static class StrictMap<V> extends ConcurrentHashMap<String, V> {

  947.     private static final long serialVersionUID = -4950446264854982944L;
  948.     private final String name;
  949.     private BiFunction<V, V, String> conflictMessageProducer;

  950.     public StrictMap(String name, int initialCapacity, float loadFactor) {
  951.       super(initialCapacity, loadFactor);
  952.       this.name = name;
  953.     }

  954.     public StrictMap(String name, int initialCapacity) {
  955.       super(initialCapacity);
  956.       this.name = name;
  957.     }

  958.     public StrictMap(String name) {
  959.       this.name = name;
  960.     }

  961.     public StrictMap(String name, Map<String, ? extends V> m) {
  962.       super(m);
  963.       this.name = name;
  964.     }

  965.     /**
  966.      * Assign a function for producing a conflict error message when contains value with the same key.
  967.      * <p>
  968.      * function arguments are 1st is saved value and 2nd is target value.
  969.      *
  970.      * @param conflictMessageProducer
  971.      *          A function for producing a conflict error message
  972.      *
  973.      * @return a conflict error message
  974.      *
  975.      * @since 3.5.0
  976.      */
  977.     public StrictMap<V> conflictMessageProducer(BiFunction<V, V, String> conflictMessageProducer) {
  978.       this.conflictMessageProducer = conflictMessageProducer;
  979.       return this;
  980.     }

  981.     @Override
  982.     @SuppressWarnings("unchecked")
  983.     public V put(String key, V value) {
  984.       if (containsKey(key)) {
  985.         throw new IllegalArgumentException(name + " already contains key " + key
  986.             + (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
  987.       }
  988.       if (key.contains(".")) {
  989.         final String shortKey = getShortName(key);
  990.         if (super.get(shortKey) == null) {
  991.           super.put(shortKey, value);
  992.         } else {
  993.           super.put(shortKey, (V) new Ambiguity(shortKey));
  994.         }
  995.       }
  996.       return super.put(key, value);
  997.     }

  998.     @Override
  999.     public boolean containsKey(Object key) {
  1000.       if (key == null) {
  1001.         return false;
  1002.       }

  1003.       return super.get(key) != null;
  1004.     }

  1005.     @Override
  1006.     public V get(Object key) {
  1007.       V value = super.get(key);
  1008.       if (value == null) {
  1009.         throw new IllegalArgumentException(name + " does not contain value for " + key);
  1010.       }
  1011.       if (value instanceof Ambiguity) {
  1012.         throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
  1013.             + " (try using the full name including the namespace, or rename one of the entries)");
  1014.       }
  1015.       return value;
  1016.     }

  1017.     protected static class Ambiguity {
  1018.       private final String subject;

  1019.       public Ambiguity(String subject) {
  1020.         this.subject = subject;
  1021.       }

  1022.       public String getSubject() {
  1023.         return subject;
  1024.       }
  1025.     }

  1026.     private String getShortName(String key) {
  1027.       final String[] keyParts = key.split("\\.");
  1028.       return keyParts[keyParts.length - 1];
  1029.     }
  1030.   }

  1031. }