Configuration.java

  1. /*
  2.  *    Copyright 2009-2022 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. 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.function.BiFunction;

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

  95. /**
  96.  * @author Clinton Begin
  97.  */
  98. public class Configuration {

  99.   protected Environment environment;

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

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

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

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

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

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

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

  153.   protected final Set<String> loadedResources = new HashSet<>();
  154.   protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");

  155.   protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
  156.   protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
  157.   protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
  158.   protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();

  159.   /*
  160.    * A map holds cache-ref relationship. The key is the namespace that
  161.    * references a cache bound to another namespace and the value is the
  162.    * namespace which the actual cache is bound to.
  163.    */
  164.   protected final Map<String, String> cacheRefMap = new HashMap<>();

  165.   public Configuration(Environment environment) {
  166.     this();
  167.     this.environment = environment;
  168.   }

  169.   public Configuration() {
  170.     typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
  171.     typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

  172.     typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  173.     typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  174.     typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

  175.     typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
  176.     typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
  177.     typeAliasRegistry.registerAlias("LRU", LruCache.class);
  178.     typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
  179.     typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

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

  181.     typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
  182.     typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

  183.     typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
  184.     typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
  185.     typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
  186.     typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
  187.     typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
  188.     typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
  189.     typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

  190.     typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
  191.     typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

  192.     languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
  193.     languageRegistry.register(RawLanguageDriver.class);
  194.   }

  195.   public String getLogPrefix() {
  196.     return logPrefix;
  197.   }

  198.   public void setLogPrefix(String logPrefix) {
  199.     this.logPrefix = logPrefix;
  200.   }

  201.   public Class<? extends Log> getLogImpl() {
  202.     return logImpl;
  203.   }

  204.   public void setLogImpl(Class<? extends Log> logImpl) {
  205.     if (logImpl != null) {
  206.       this.logImpl = logImpl;
  207.       LogFactory.useCustomLogging(this.logImpl);
  208.     }
  209.   }

  210.   public Class<? extends VFS> getVfsImpl() {
  211.     return this.vfsImpl;
  212.   }

  213.   public void setVfsImpl(Class<? extends VFS> vfsImpl) {
  214.     if (vfsImpl != null) {
  215.       this.vfsImpl = vfsImpl;
  216.       VFS.addImplClass(this.vfsImpl);
  217.     }
  218.   }

  219.   /**
  220.    * Gets an applying type when omit a type on sql provider annotation(e.g. {@link org.apache.ibatis.annotations.SelectProvider}).
  221.    *
  222.    * @return the default type for sql provider annotation
  223.    * @since 3.5.6
  224.    */
  225.   public Class<?> getDefaultSqlProviderType() {
  226.     return defaultSqlProviderType;
  227.   }

  228.   /**
  229.    * Sets an applying type when omit a type on sql provider annotation(e.g. {@link org.apache.ibatis.annotations.SelectProvider}).
  230.    *
  231.    * @param defaultSqlProviderType
  232.    *          the default type for sql provider annotation
  233.    * @since 3.5.6
  234.    */
  235.   public void setDefaultSqlProviderType(Class<?> defaultSqlProviderType) {
  236.     this.defaultSqlProviderType = defaultSqlProviderType;
  237.   }

  238.   public boolean isCallSettersOnNulls() {
  239.     return callSettersOnNulls;
  240.   }

  241.   public void setCallSettersOnNulls(boolean callSettersOnNulls) {
  242.     this.callSettersOnNulls = callSettersOnNulls;
  243.   }

  244.   public boolean isUseActualParamName() {
  245.     return useActualParamName;
  246.   }

  247.   public void setUseActualParamName(boolean useActualParamName) {
  248.     this.useActualParamName = useActualParamName;
  249.   }

  250.   public boolean isReturnInstanceForEmptyRow() {
  251.     return returnInstanceForEmptyRow;
  252.   }

  253.   public void setReturnInstanceForEmptyRow(boolean returnEmptyInstance) {
  254.     this.returnInstanceForEmptyRow = returnEmptyInstance;
  255.   }

  256.   public boolean isShrinkWhitespacesInSql() {
  257.     return shrinkWhitespacesInSql;
  258.   }

  259.   public void setShrinkWhitespacesInSql(boolean shrinkWhitespacesInSql) {
  260.     this.shrinkWhitespacesInSql = shrinkWhitespacesInSql;
  261.   }

  262.   /**
  263.    * Sets the default value of 'nullable' attribute on 'foreach' tag.
  264.    *
  265.    * @param nullableOnForEach If nullable, set to {@code true}
  266.    * @since 3.5.9
  267.    */
  268.   public void setNullableOnForEach(boolean nullableOnForEach) {
  269.     this.nullableOnForEach = nullableOnForEach;
  270.   }

  271.   /**
  272.    * Returns the default value of 'nullable' attribute on 'foreach' tag.
  273.    *
  274.    * <p>Default is {@code false}.
  275.    *
  276.    * @return If nullable, set to {@code true}
  277.    * @since 3.5.9
  278.    */
  279.   public boolean isNullableOnForEach() {
  280.     return nullableOnForEach;
  281.   }

  282.   public boolean isArgNameBasedConstructorAutoMapping() {
  283.     return argNameBasedConstructorAutoMapping;
  284.   }

  285.   public void setArgNameBasedConstructorAutoMapping(boolean argNameBasedConstructorAutoMapping) {
  286.     this.argNameBasedConstructorAutoMapping = argNameBasedConstructorAutoMapping;
  287.   }

  288.   public String getDatabaseId() {
  289.     return databaseId;
  290.   }

  291.   public void setDatabaseId(String databaseId) {
  292.     this.databaseId = databaseId;
  293.   }

  294.   public Class<?> getConfigurationFactory() {
  295.     return configurationFactory;
  296.   }

  297.   public void setConfigurationFactory(Class<?> configurationFactory) {
  298.     this.configurationFactory = configurationFactory;
  299.   }

  300.   public boolean isSafeResultHandlerEnabled() {
  301.     return safeResultHandlerEnabled;
  302.   }

  303.   public void setSafeResultHandlerEnabled(boolean safeResultHandlerEnabled) {
  304.     this.safeResultHandlerEnabled = safeResultHandlerEnabled;
  305.   }

  306.   public boolean isSafeRowBoundsEnabled() {
  307.     return safeRowBoundsEnabled;
  308.   }

  309.   public void setSafeRowBoundsEnabled(boolean safeRowBoundsEnabled) {
  310.     this.safeRowBoundsEnabled = safeRowBoundsEnabled;
  311.   }

  312.   public boolean isMapUnderscoreToCamelCase() {
  313.     return mapUnderscoreToCamelCase;
  314.   }

  315.   public void setMapUnderscoreToCamelCase(boolean mapUnderscoreToCamelCase) {
  316.     this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase;
  317.   }

  318.   public void addLoadedResource(String resource) {
  319.     loadedResources.add(resource);
  320.   }

  321.   public boolean isResourceLoaded(String resource) {
  322.     return loadedResources.contains(resource);
  323.   }

  324.   public Environment getEnvironment() {
  325.     return environment;
  326.   }

  327.   public void setEnvironment(Environment environment) {
  328.     this.environment = environment;
  329.   }

  330.   public AutoMappingBehavior getAutoMappingBehavior() {
  331.     return autoMappingBehavior;
  332.   }

  333.   public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) {
  334.     this.autoMappingBehavior = autoMappingBehavior;
  335.   }

  336.   /**
  337.    * Gets the auto mapping unknown column behavior.
  338.    *
  339.    * @return the auto mapping unknown column behavior
  340.    * @since 3.4.0
  341.    */
  342.   public AutoMappingUnknownColumnBehavior getAutoMappingUnknownColumnBehavior() {
  343.     return autoMappingUnknownColumnBehavior;
  344.   }

  345.   /**
  346.    * Sets the auto mapping unknown column behavior.
  347.    *
  348.    * @param autoMappingUnknownColumnBehavior
  349.    *          the new auto mapping unknown column behavior
  350.    * @since 3.4.0
  351.    */
  352.   public void setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior) {
  353.     this.autoMappingUnknownColumnBehavior = autoMappingUnknownColumnBehavior;
  354.   }

  355.   public boolean isLazyLoadingEnabled() {
  356.     return lazyLoadingEnabled;
  357.   }

  358.   public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) {
  359.     this.lazyLoadingEnabled = lazyLoadingEnabled;
  360.   }

  361.   public ProxyFactory getProxyFactory() {
  362.     return proxyFactory;
  363.   }

  364.   public void setProxyFactory(ProxyFactory proxyFactory) {
  365.     if (proxyFactory == null) {
  366.       proxyFactory = new JavassistProxyFactory();
  367.     }
  368.     this.proxyFactory = proxyFactory;
  369.   }

  370.   public boolean isAggressiveLazyLoading() {
  371.     return aggressiveLazyLoading;
  372.   }

  373.   public void setAggressiveLazyLoading(boolean aggressiveLazyLoading) {
  374.     this.aggressiveLazyLoading = aggressiveLazyLoading;
  375.   }

  376.   public boolean isMultipleResultSetsEnabled() {
  377.     return multipleResultSetsEnabled;
  378.   }

  379.   public void setMultipleResultSetsEnabled(boolean multipleResultSetsEnabled) {
  380.     this.multipleResultSetsEnabled = multipleResultSetsEnabled;
  381.   }

  382.   public Set<String> getLazyLoadTriggerMethods() {
  383.     return lazyLoadTriggerMethods;
  384.   }

  385.   public void setLazyLoadTriggerMethods(Set<String> lazyLoadTriggerMethods) {
  386.     this.lazyLoadTriggerMethods = lazyLoadTriggerMethods;
  387.   }

  388.   public boolean isUseGeneratedKeys() {
  389.     return useGeneratedKeys;
  390.   }

  391.   public void setUseGeneratedKeys(boolean useGeneratedKeys) {
  392.     this.useGeneratedKeys = useGeneratedKeys;
  393.   }

  394.   public ExecutorType getDefaultExecutorType() {
  395.     return defaultExecutorType;
  396.   }

  397.   public void setDefaultExecutorType(ExecutorType defaultExecutorType) {
  398.     this.defaultExecutorType = defaultExecutorType;
  399.   }

  400.   public boolean isCacheEnabled() {
  401.     return cacheEnabled;
  402.   }

  403.   public void setCacheEnabled(boolean cacheEnabled) {
  404.     this.cacheEnabled = cacheEnabled;
  405.   }

  406.   public Integer getDefaultStatementTimeout() {
  407.     return defaultStatementTimeout;
  408.   }

  409.   public void setDefaultStatementTimeout(Integer defaultStatementTimeout) {
  410.     this.defaultStatementTimeout = defaultStatementTimeout;
  411.   }

  412.   /**
  413.    * Gets the default fetch size.
  414.    *
  415.    * @return the default fetch size
  416.    * @since 3.3.0
  417.    */
  418.   public Integer getDefaultFetchSize() {
  419.     return defaultFetchSize;
  420.   }

  421.   /**
  422.    * Sets the default fetch size.
  423.    *
  424.    * @param defaultFetchSize
  425.    *          the new default fetch size
  426.    * @since 3.3.0
  427.    */
  428.   public void setDefaultFetchSize(Integer defaultFetchSize) {
  429.     this.defaultFetchSize = defaultFetchSize;
  430.   }

  431.   /**
  432.    * Gets the default result set type.
  433.    *
  434.    * @return the default result set type
  435.    * @since 3.5.2
  436.    */
  437.   public ResultSetType getDefaultResultSetType() {
  438.     return defaultResultSetType;
  439.   }

  440.   /**
  441.    * Sets the default result set type.
  442.    *
  443.    * @param defaultResultSetType
  444.    *          the new default result set type
  445.    * @since 3.5.2
  446.    */
  447.   public void setDefaultResultSetType(ResultSetType defaultResultSetType) {
  448.     this.defaultResultSetType = defaultResultSetType;
  449.   }

  450.   public boolean isUseColumnLabel() {
  451.     return useColumnLabel;
  452.   }

  453.   public void setUseColumnLabel(boolean useColumnLabel) {
  454.     this.useColumnLabel = useColumnLabel;
  455.   }

  456.   public LocalCacheScope getLocalCacheScope() {
  457.     return localCacheScope;
  458.   }

  459.   public void setLocalCacheScope(LocalCacheScope localCacheScope) {
  460.     this.localCacheScope = localCacheScope;
  461.   }

  462.   public JdbcType getJdbcTypeForNull() {
  463.     return jdbcTypeForNull;
  464.   }

  465.   public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) {
  466.     this.jdbcTypeForNull = jdbcTypeForNull;
  467.   }

  468.   public Properties getVariables() {
  469.     return variables;
  470.   }

  471.   public void setVariables(Properties variables) {
  472.     this.variables = variables;
  473.   }

  474.   public TypeHandlerRegistry getTypeHandlerRegistry() {
  475.     return typeHandlerRegistry;
  476.   }

  477.   /**
  478.    * Set a default {@link TypeHandler} class for {@link Enum}.
  479.    * A default {@link TypeHandler} is {@link org.apache.ibatis.type.EnumTypeHandler}.
  480.    * @param typeHandler a type handler class for {@link Enum}
  481.    * @since 3.4.5
  482.    */
  483.   public void setDefaultEnumTypeHandler(Class<? extends TypeHandler> typeHandler) {
  484.     if (typeHandler != null) {
  485.       getTypeHandlerRegistry().setDefaultEnumTypeHandler(typeHandler);
  486.     }
  487.   }

  488.   public TypeAliasRegistry getTypeAliasRegistry() {
  489.     return typeAliasRegistry;
  490.   }

  491.   /**
  492.    * Gets the mapper registry.
  493.    *
  494.    * @return the mapper registry
  495.    * @since 3.2.2
  496.    */
  497.   public MapperRegistry getMapperRegistry() {
  498.     return mapperRegistry;
  499.   }

  500.   public ReflectorFactory getReflectorFactory() {
  501.     return reflectorFactory;
  502.   }

  503.   public void setReflectorFactory(ReflectorFactory reflectorFactory) {
  504.     this.reflectorFactory = reflectorFactory;
  505.   }

  506.   public ObjectFactory getObjectFactory() {
  507.     return objectFactory;
  508.   }

  509.   public void setObjectFactory(ObjectFactory objectFactory) {
  510.     this.objectFactory = objectFactory;
  511.   }

  512.   public ObjectWrapperFactory getObjectWrapperFactory() {
  513.     return objectWrapperFactory;
  514.   }

  515.   public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
  516.     this.objectWrapperFactory = objectWrapperFactory;
  517.   }

  518.   /**
  519.    * Gets the interceptors.
  520.    *
  521.    * @return the interceptors
  522.    * @since 3.2.2
  523.    */
  524.   public List<Interceptor> getInterceptors() {
  525.     return interceptorChain.getInterceptors();
  526.   }

  527.   public LanguageDriverRegistry getLanguageRegistry() {
  528.     return languageRegistry;
  529.   }

  530.   public void setDefaultScriptingLanguage(Class<? extends LanguageDriver> driver) {
  531.     if (driver == null) {
  532.       driver = XMLLanguageDriver.class;
  533.     }
  534.     getLanguageRegistry().setDefaultDriverClass(driver);
  535.   }

  536.   public LanguageDriver getDefaultScriptingLanguageInstance() {
  537.     return languageRegistry.getDefaultDriver();
  538.   }

  539.   /**
  540.    * Gets the language driver.
  541.    *
  542.    * @param langClass
  543.    *          the lang class
  544.    * @return the language driver
  545.    * @since 3.5.1
  546.    */
  547.   public LanguageDriver getLanguageDriver(Class<? extends LanguageDriver> langClass) {
  548.     if (langClass == null) {
  549.       return languageRegistry.getDefaultDriver();
  550.     }
  551.     languageRegistry.register(langClass);
  552.     return languageRegistry.getDriver(langClass);
  553.   }

  554.   /**
  555.    * Gets the default scripting language instance.
  556.    *
  557.    * @return the default scripting language instance
  558.    * @deprecated Use {@link #getDefaultScriptingLanguageInstance()}
  559.    */
  560.   @Deprecated
  561.   public LanguageDriver getDefaultScriptingLanuageInstance() {
  562.     return getDefaultScriptingLanguageInstance();
  563.   }

  564.   public MetaObject newMetaObject(Object object) {
  565.     return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
  566.   }

  567.   public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  568.     ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
  569.     parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  570.     return parameterHandler;
  571.   }

  572.   public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
  573.       ResultHandler resultHandler, BoundSql boundSql) {
  574.     ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
  575.     resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
  576.     return resultSetHandler;
  577.   }

  578.   public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  579.     StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  580.     statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  581.     return statementHandler;
  582.   }

  583.   public Executor newExecutor(Transaction transaction) {
  584.     return newExecutor(transaction, defaultExecutorType);
  585.   }

  586.   public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  587.     executorType = executorType == null ? defaultExecutorType : executorType;
  588.     executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  589.     Executor executor;
  590.     if (ExecutorType.BATCH == executorType) {
  591.       executor = new BatchExecutor(this, transaction);
  592.     } else if (ExecutorType.REUSE == executorType) {
  593.       executor = new ReuseExecutor(this, transaction);
  594.     } else {
  595.       executor = new SimpleExecutor(this, transaction);
  596.     }
  597.     if (cacheEnabled) {
  598.       executor = new CachingExecutor(executor);
  599.     }
  600.     executor = (Executor) interceptorChain.pluginAll(executor);
  601.     return executor;
  602.   }

  603.   public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
  604.     keyGenerators.put(id, keyGenerator);
  605.   }

  606.   public Collection<String> getKeyGeneratorNames() {
  607.     return keyGenerators.keySet();
  608.   }

  609.   public Collection<KeyGenerator> getKeyGenerators() {
  610.     return keyGenerators.values();
  611.   }

  612.   public KeyGenerator getKeyGenerator(String id) {
  613.     return keyGenerators.get(id);
  614.   }

  615.   public boolean hasKeyGenerator(String id) {
  616.     return keyGenerators.containsKey(id);
  617.   }

  618.   public void addCache(Cache cache) {
  619.     caches.put(cache.getId(), cache);
  620.   }

  621.   public Collection<String> getCacheNames() {
  622.     return caches.keySet();
  623.   }

  624.   public Collection<Cache> getCaches() {
  625.     return caches.values();
  626.   }

  627.   public Cache getCache(String id) {
  628.     return caches.get(id);
  629.   }

  630.   public boolean hasCache(String id) {
  631.     return caches.containsKey(id);
  632.   }

  633.   public void addResultMap(ResultMap rm) {
  634.     resultMaps.put(rm.getId(), rm);
  635.     checkLocallyForDiscriminatedNestedResultMaps(rm);
  636.     checkGloballyForDiscriminatedNestedResultMaps(rm);
  637.   }

  638.   public Collection<String> getResultMapNames() {
  639.     return resultMaps.keySet();
  640.   }

  641.   public Collection<ResultMap> getResultMaps() {
  642.     return resultMaps.values();
  643.   }

  644.   public ResultMap getResultMap(String id) {
  645.     return resultMaps.get(id);
  646.   }

  647.   public boolean hasResultMap(String id) {
  648.     return resultMaps.containsKey(id);
  649.   }

  650.   public void addParameterMap(ParameterMap pm) {
  651.     parameterMaps.put(pm.getId(), pm);
  652.   }

  653.   public Collection<String> getParameterMapNames() {
  654.     return parameterMaps.keySet();
  655.   }

  656.   public Collection<ParameterMap> getParameterMaps() {
  657.     return parameterMaps.values();
  658.   }

  659.   public ParameterMap getParameterMap(String id) {
  660.     return parameterMaps.get(id);
  661.   }

  662.   public boolean hasParameterMap(String id) {
  663.     return parameterMaps.containsKey(id);
  664.   }

  665.   public void addMappedStatement(MappedStatement ms) {
  666.     mappedStatements.put(ms.getId(), ms);
  667.   }

  668.   public Collection<String> getMappedStatementNames() {
  669.     buildAllStatements();
  670.     return mappedStatements.keySet();
  671.   }

  672.   public Collection<MappedStatement> getMappedStatements() {
  673.     buildAllStatements();
  674.     return mappedStatements.values();
  675.   }

  676.   public Collection<XMLStatementBuilder> getIncompleteStatements() {
  677.     return incompleteStatements;
  678.   }

  679.   public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
  680.     incompleteStatements.add(incompleteStatement);
  681.   }

  682.   public Collection<CacheRefResolver> getIncompleteCacheRefs() {
  683.     return incompleteCacheRefs;
  684.   }

  685.   public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
  686.     incompleteCacheRefs.add(incompleteCacheRef);
  687.   }

  688.   public Collection<ResultMapResolver> getIncompleteResultMaps() {
  689.     return incompleteResultMaps;
  690.   }

  691.   public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
  692.     incompleteResultMaps.add(resultMapResolver);
  693.   }

  694.   public void addIncompleteMethod(MethodResolver builder) {
  695.     incompleteMethods.add(builder);
  696.   }

  697.   public Collection<MethodResolver> getIncompleteMethods() {
  698.     return incompleteMethods;
  699.   }

  700.   public MappedStatement getMappedStatement(String id) {
  701.     return this.getMappedStatement(id, true);
  702.   }

  703.   public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
  704.     if (validateIncompleteStatements) {
  705.       buildAllStatements();
  706.     }
  707.     return mappedStatements.get(id);
  708.   }

  709.   public Map<String, XNode> getSqlFragments() {
  710.     return sqlFragments;
  711.   }

  712.   public void addInterceptor(Interceptor interceptor) {
  713.     interceptorChain.addInterceptor(interceptor);
  714.   }

  715.   public void addMappers(String packageName, Class<?> superType) {
  716.     mapperRegistry.addMappers(packageName, superType);
  717.   }

  718.   public void addMappers(String packageName) {
  719.     mapperRegistry.addMappers(packageName);
  720.   }

  721.   public <T> void addMapper(Class<T> type) {
  722.     mapperRegistry.addMapper(type);
  723.   }

  724.   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  725.     return mapperRegistry.getMapper(type, sqlSession);
  726.   }

  727.   public boolean hasMapper(Class<?> type) {
  728.     return mapperRegistry.hasMapper(type);
  729.   }

  730.   public boolean hasStatement(String statementName) {
  731.     return hasStatement(statementName, true);
  732.   }

  733.   public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
  734.     if (validateIncompleteStatements) {
  735.       buildAllStatements();
  736.     }
  737.     return mappedStatements.containsKey(statementName);
  738.   }

  739.   public void addCacheRef(String namespace, String referencedNamespace) {
  740.     cacheRefMap.put(namespace, referencedNamespace);
  741.   }

  742.   /*
  743.    * Parses all the unprocessed statement nodes in the cache. It is recommended
  744.    * to call this method once all the mappers are added as it provides fail-fast
  745.    * statement validation.
  746.    */
  747.   protected void buildAllStatements() {
  748.     parsePendingResultMaps();
  749.     if (!incompleteCacheRefs.isEmpty()) {
  750.       synchronized (incompleteCacheRefs) {
  751.         incompleteCacheRefs.removeIf(x -> x.resolveCacheRef() != null);
  752.       }
  753.     }
  754.     if (!incompleteStatements.isEmpty()) {
  755.       synchronized (incompleteStatements) {
  756.         incompleteStatements.removeIf(x -> {
  757.           x.parseStatementNode();
  758.           return true;
  759.         });
  760.       }
  761.     }
  762.     if (!incompleteMethods.isEmpty()) {
  763.       synchronized (incompleteMethods) {
  764.         incompleteMethods.removeIf(x -> {
  765.           x.resolve();
  766.           return true;
  767.         });
  768.       }
  769.     }
  770.   }

  771.   private void parsePendingResultMaps() {
  772.     if (incompleteResultMaps.isEmpty()) {
  773.       return;
  774.     }
  775.     synchronized (incompleteResultMaps) {
  776.       boolean resolved;
  777.       IncompleteElementException ex = null;
  778.       do {
  779.         resolved = false;
  780.         Iterator<ResultMapResolver> iterator = incompleteResultMaps.iterator();
  781.         while (iterator.hasNext()) {
  782.           try {
  783.             iterator.next().resolve();
  784.             iterator.remove();
  785.             resolved = true;
  786.           } catch (IncompleteElementException e) {
  787.             ex = e;
  788.           }
  789.         }
  790.       } while (resolved);
  791.       if (!incompleteResultMaps.isEmpty() && ex != null) {
  792.         // At least one result map is unresolvable.
  793.         throw ex;
  794.       }
  795.     }
  796.   }

  797.   /**
  798.    * Extracts namespace from fully qualified statement id.
  799.    *
  800.    * @param statementId
  801.    *          the statement id
  802.    * @return namespace or null when id does not contain period.
  803.    */
  804.   protected String extractNamespace(String statementId) {
  805.     int lastPeriod = statementId.lastIndexOf('.');
  806.     return lastPeriod > 0 ? statementId.substring(0, lastPeriod) : null;
  807.   }

  808.   // Slow but a one time cost. A better solution is welcome.
  809.   protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
  810.     if (rm.hasNestedResultMaps()) {
  811.       for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
  812.         Object value = entry.getValue();
  813.         if (value instanceof ResultMap) {
  814.           ResultMap entryResultMap = (ResultMap) value;
  815.           if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {
  816.             Collection<String> discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap().values();
  817.             if (discriminatedResultMapNames.contains(rm.getId())) {
  818.               entryResultMap.forceNestedResultMaps();
  819.             }
  820.           }
  821.         }
  822.       }
  823.     }
  824.   }

  825.   // Slow but a one time cost. A better solution is welcome.
  826.   protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
  827.     if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
  828.       for (Map.Entry<String, String> entry : rm.getDiscriminator().getDiscriminatorMap().entrySet()) {
  829.         String discriminatedResultMapName = entry.getValue();
  830.         if (hasResultMap(discriminatedResultMapName)) {
  831.           ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);
  832.           if (discriminatedResultMap.hasNestedResultMaps()) {
  833.             rm.forceNestedResultMaps();
  834.             break;
  835.           }
  836.         }
  837.       }
  838.     }
  839.   }

  840.   protected static class StrictMap<V> extends HashMap<String, V> {

  841.     private static final long serialVersionUID = -4950446264854982944L;
  842.     private final String name;
  843.     private BiFunction<V, V, String> conflictMessageProducer;

  844.     public StrictMap(String name, int initialCapacity, float loadFactor) {
  845.       super(initialCapacity, loadFactor);
  846.       this.name = name;
  847.     }

  848.     public StrictMap(String name, int initialCapacity) {
  849.       super(initialCapacity);
  850.       this.name = name;
  851.     }

  852.     public StrictMap(String name) {
  853.       super();
  854.       this.name = name;
  855.     }

  856.     public StrictMap(String name, Map<String, ? extends V> m) {
  857.       super(m);
  858.       this.name = name;
  859.     }

  860.     /**
  861.      * Assign a function for producing a conflict error message when contains value with the same key.
  862.      * <p>
  863.      * function arguments are 1st is saved value and 2nd is target value.
  864.      * @param conflictMessageProducer A function for producing a conflict error message
  865.      * @return a conflict error message
  866.      * @since 3.5.0
  867.      */
  868.     public StrictMap<V> conflictMessageProducer(BiFunction<V, V, String> conflictMessageProducer) {
  869.       this.conflictMessageProducer = conflictMessageProducer;
  870.       return this;
  871.     }

  872.     @Override
  873.     @SuppressWarnings("unchecked")
  874.     public V put(String key, V value) {
  875.       if (containsKey(key)) {
  876.         throw new IllegalArgumentException(name + " already contains value for " + key
  877.             + (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
  878.       }
  879.       if (key.contains(".")) {
  880.         final String shortKey = getShortName(key);
  881.         if (super.get(shortKey) == null) {
  882.           super.put(shortKey, value);
  883.         } else {
  884.           super.put(shortKey, (V) new Ambiguity(shortKey));
  885.         }
  886.       }
  887.       return super.put(key, value);
  888.     }

  889.     @Override
  890.     public V get(Object key) {
  891.       V value = super.get(key);
  892.       if (value == null) {
  893.         throw new IllegalArgumentException(name + " does not contain value for " + key);
  894.       }
  895.       if (value instanceof Ambiguity) {
  896.         throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
  897.             + " (try using the full name including the namespace, or rename one of the entries)");
  898.       }
  899.       return value;
  900.     }

  901.     protected static class Ambiguity {
  902.       private final String subject;

  903.       public Ambiguity(String subject) {
  904.         this.subject = subject;
  905.       }

  906.       public String getSubject() {
  907.         return subject;
  908.       }
  909.     }

  910.     private String getShortName(String key) {
  911.       final String[] keyParts = key.split("\\.");
  912.       return keyParts[keyParts.length - 1];
  913.     }
  914.   }

  915. }