All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.ibatis.session.Configuration Maven / Gradle / Ivy

The newest version!
/*
 *    Copyright 2009-2024 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       https://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.session;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiFunction;

import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.builder.CacheRefResolver;
import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.builder.ResultMapResolver;
import org.apache.ibatis.builder.annotation.MethodResolver;
import org.apache.ibatis.builder.xml.XMLStatementBuilder;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.decorators.FifoCache;
import org.apache.ibatis.cache.decorators.LruCache;
import org.apache.ibatis.cache.decorators.SoftCache;
import org.apache.ibatis.cache.decorators.WeakCache;
import org.apache.ibatis.cache.impl.PerpetualCache;
import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import org.apache.ibatis.executor.BatchExecutor;
import org.apache.ibatis.executor.CachingExecutor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ReuseExecutor;
import org.apache.ibatis.executor.SimpleExecutor;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.loader.ProxyFactory;
import org.apache.ibatis.executor.loader.cglib.CglibProxyFactory;
import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
import org.apache.ibatis.logging.log4j.Log4jImpl;
import org.apache.ibatis.logging.log4j2.Log4j2Impl;
import org.apache.ibatis.logging.nologging.NoLoggingImpl;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.InterceptorChain;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.LanguageDriverRegistry;
import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * @author Clinton Begin
 */
public class Configuration {

  protected Environment environment;

  protected boolean safeRowBoundsEnabled;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase;
  protected boolean aggressiveLazyLoading;
  protected boolean multipleResultSetsEnabled = true;
  protected boolean useGeneratedKeys;
  protected boolean useColumnLabel = true;
  protected boolean cacheEnabled = true;
  protected boolean callSettersOnNulls;
  protected boolean useActualParamName = true;
  protected boolean returnInstanceForEmptyRow;
  protected boolean shrinkWhitespacesInSql;
  protected boolean nullableOnForEach;
  protected boolean argNameBasedConstructorAutoMapping;

  protected String logPrefix;
  protected Class logImpl;
  protected Class vfsImpl;
  protected Class defaultSqlProviderType;
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  protected Set lazyLoadTriggerMethods = new HashSet<>(
      Arrays.asList("equals", "clone", "hashCode", "toString"));
  protected Integer defaultStatementTimeout;
  protected Integer defaultFetchSize;
  protected ResultSetType defaultResultSetType;
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;

  protected Properties variables = new Properties();
  protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  protected ObjectFactory objectFactory = new DefaultObjectFactory();
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

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

  protected String databaseId;
  /**
   * Configuration factory class. Used to create Configuration for loading deserialized unread properties.
   *
   * @see Issue 300 (google code)
   */
  protected Class configurationFactory;

  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  protected final Map mappedStatements = new StrictMap(
      "Mapped Statements collection")
          .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and "
              + targetValue.getResource());
  protected final Map caches = new StrictMap<>("Caches collection");
  protected final Map resultMaps = new StrictMap<>("Result Maps collection");
  protected final Map parameterMaps = new StrictMap<>("Parameter Maps collection");
  protected final Map keyGenerators = new StrictMap<>("Key Generators collection");

  protected final Set loadedResources = new HashSet<>();
  protected final Map sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
  protected final Collection incompleteStatements = new LinkedList<>();
  protected final Collection incompleteCacheRefs = new LinkedList<>();
  protected final Collection incompleteResultMaps = new LinkedList<>();
  protected final Collection incompleteMethods = new LinkedList<>();

  private final ReentrantLock incompleteResultMapsLock = new ReentrantLock();
  private final ReentrantLock incompleteCacheRefsLock = new ReentrantLock();
  private final ReentrantLock incompleteStatementsLock = new ReentrantLock();
  private final ReentrantLock incompleteMethodsLock = new ReentrantLock();

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

  public Configuration(Environment environment) {
    this();
    this.environment = environment;
  }

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

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

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

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

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

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

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

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

  public String getLogPrefix() {
    return logPrefix;
  }

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

  public Class getLogImpl() {
    return logImpl;
  }

  public void setLogImpl(Class logImpl) {
    if (logImpl != null) {
      this.logImpl = logImpl;
      LogFactory.useCustomLogging(this.logImpl);
    }
  }

  public Class getVfsImpl() {
    return this.vfsImpl;
  }

  public void setVfsImpl(Class vfsImpl) {
    if (vfsImpl != null) {
      this.vfsImpl = vfsImpl;
      VFS.addImplClass(this.vfsImpl);
    }
  }

  /**
   * Gets an applying type when omit a type on sql provider annotation(e.g.
   * {@link org.apache.ibatis.annotations.SelectProvider}).
   *
   * @return the default type for sql provider annotation
   *
   * @since 3.5.6
   */
  public Class getDefaultSqlProviderType() {
    return defaultSqlProviderType;
  }

  /**
   * Sets an applying type when omit a type on sql provider annotation(e.g.
   * {@link org.apache.ibatis.annotations.SelectProvider}).
   *
   * @param defaultSqlProviderType
   *          the default type for sql provider annotation
   *
   * @since 3.5.6
   */
  public void setDefaultSqlProviderType(Class defaultSqlProviderType) {
    this.defaultSqlProviderType = defaultSqlProviderType;
  }

  public boolean isCallSettersOnNulls() {
    return callSettersOnNulls;
  }

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

  public boolean isUseActualParamName() {
    return useActualParamName;
  }

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

  public boolean isReturnInstanceForEmptyRow() {
    return returnInstanceForEmptyRow;
  }

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

  public boolean isShrinkWhitespacesInSql() {
    return shrinkWhitespacesInSql;
  }

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

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

  /**
   * Returns the default value of 'nullable' attribute on 'foreach' tag.
   * 

* Default is {@code false}. * * @return If nullable, set to {@code true} * * @since 3.5.9 */ public boolean isNullableOnForEach() { return nullableOnForEach; } public boolean isArgNameBasedConstructorAutoMapping() { return argNameBasedConstructorAutoMapping; } public void setArgNameBasedConstructorAutoMapping(boolean argNameBasedConstructorAutoMapping) { this.argNameBasedConstructorAutoMapping = argNameBasedConstructorAutoMapping; } public String getDatabaseId() { return databaseId; } public void setDatabaseId(String databaseId) { this.databaseId = databaseId; } public Class getConfigurationFactory() { return configurationFactory; } public void setConfigurationFactory(Class configurationFactory) { this.configurationFactory = configurationFactory; } public boolean isSafeResultHandlerEnabled() { return safeResultHandlerEnabled; } public void setSafeResultHandlerEnabled(boolean safeResultHandlerEnabled) { this.safeResultHandlerEnabled = safeResultHandlerEnabled; } public boolean isSafeRowBoundsEnabled() { return safeRowBoundsEnabled; } public void setSafeRowBoundsEnabled(boolean safeRowBoundsEnabled) { this.safeRowBoundsEnabled = safeRowBoundsEnabled; } public boolean isMapUnderscoreToCamelCase() { return mapUnderscoreToCamelCase; } public void setMapUnderscoreToCamelCase(boolean mapUnderscoreToCamelCase) { this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase; } public void addLoadedResource(String resource) { loadedResources.add(resource); } public boolean isResourceLoaded(String resource) { return loadedResources.contains(resource); } public Environment getEnvironment() { return environment; } public void setEnvironment(Environment environment) { this.environment = environment; } public AutoMappingBehavior getAutoMappingBehavior() { return autoMappingBehavior; } public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) { this.autoMappingBehavior = autoMappingBehavior; } /** * Gets the auto mapping unknown column behavior. * * @return the auto mapping unknown column behavior * * @since 3.4.0 */ public AutoMappingUnknownColumnBehavior getAutoMappingUnknownColumnBehavior() { return autoMappingUnknownColumnBehavior; } /** * Sets the auto mapping unknown column behavior. * * @param autoMappingUnknownColumnBehavior * the new auto mapping unknown column behavior * * @since 3.4.0 */ public void setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior) { this.autoMappingUnknownColumnBehavior = autoMappingUnknownColumnBehavior; } public boolean isLazyLoadingEnabled() { return lazyLoadingEnabled; } public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) { this.lazyLoadingEnabled = lazyLoadingEnabled; } public ProxyFactory getProxyFactory() { return proxyFactory; } public void setProxyFactory(ProxyFactory proxyFactory) { if (proxyFactory == null) { proxyFactory = new JavassistProxyFactory(); } this.proxyFactory = proxyFactory; } public boolean isAggressiveLazyLoading() { return aggressiveLazyLoading; } public void setAggressiveLazyLoading(boolean aggressiveLazyLoading) { this.aggressiveLazyLoading = aggressiveLazyLoading; } public boolean isMultipleResultSetsEnabled() { return multipleResultSetsEnabled; } public void setMultipleResultSetsEnabled(boolean multipleResultSetsEnabled) { this.multipleResultSetsEnabled = multipleResultSetsEnabled; } public Set getLazyLoadTriggerMethods() { return lazyLoadTriggerMethods; } public void setLazyLoadTriggerMethods(Set lazyLoadTriggerMethods) { this.lazyLoadTriggerMethods = lazyLoadTriggerMethods; } public boolean isUseGeneratedKeys() { return useGeneratedKeys; } public void setUseGeneratedKeys(boolean useGeneratedKeys) { this.useGeneratedKeys = useGeneratedKeys; } public ExecutorType getDefaultExecutorType() { return defaultExecutorType; } public void setDefaultExecutorType(ExecutorType defaultExecutorType) { this.defaultExecutorType = defaultExecutorType; } public boolean isCacheEnabled() { return cacheEnabled; } public void setCacheEnabled(boolean cacheEnabled) { this.cacheEnabled = cacheEnabled; } public Integer getDefaultStatementTimeout() { return defaultStatementTimeout; } public void setDefaultStatementTimeout(Integer defaultStatementTimeout) { this.defaultStatementTimeout = defaultStatementTimeout; } /** * Gets the default fetch size. * * @return the default fetch size * * @since 3.3.0 */ public Integer getDefaultFetchSize() { return defaultFetchSize; } /** * Sets the default fetch size. * * @param defaultFetchSize * the new default fetch size * * @since 3.3.0 */ public void setDefaultFetchSize(Integer defaultFetchSize) { this.defaultFetchSize = defaultFetchSize; } /** * Gets the default result set type. * * @return the default result set type * * @since 3.5.2 */ public ResultSetType getDefaultResultSetType() { return defaultResultSetType; } /** * Sets the default result set type. * * @param defaultResultSetType * the new default result set type * * @since 3.5.2 */ public void setDefaultResultSetType(ResultSetType defaultResultSetType) { this.defaultResultSetType = defaultResultSetType; } public boolean isUseColumnLabel() { return useColumnLabel; } public void setUseColumnLabel(boolean useColumnLabel) { this.useColumnLabel = useColumnLabel; } public LocalCacheScope getLocalCacheScope() { return localCacheScope; } public void setLocalCacheScope(LocalCacheScope localCacheScope) { this.localCacheScope = localCacheScope; } public JdbcType getJdbcTypeForNull() { return jdbcTypeForNull; } public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) { this.jdbcTypeForNull = jdbcTypeForNull; } public Properties getVariables() { return variables; } public void setVariables(Properties variables) { this.variables = variables; } public TypeHandlerRegistry getTypeHandlerRegistry() { return typeHandlerRegistry; } /** * Set a default {@link TypeHandler} class for {@link Enum}. A default {@link TypeHandler} is * {@link org.apache.ibatis.type.EnumTypeHandler}. * * @param typeHandler * a type handler class for {@link Enum} * * @since 3.4.5 */ public void setDefaultEnumTypeHandler(Class typeHandler) { if (typeHandler != null) { getTypeHandlerRegistry().setDefaultEnumTypeHandler(typeHandler); } } public TypeAliasRegistry getTypeAliasRegistry() { return typeAliasRegistry; } /** * Gets the mapper registry. * * @return the mapper registry * * @since 3.2.2 */ public MapperRegistry getMapperRegistry() { return mapperRegistry; } public ReflectorFactory getReflectorFactory() { return reflectorFactory; } public void setReflectorFactory(ReflectorFactory reflectorFactory) { this.reflectorFactory = reflectorFactory; } public ObjectFactory getObjectFactory() { return objectFactory; } public void setObjectFactory(ObjectFactory objectFactory) { this.objectFactory = objectFactory; } public ObjectWrapperFactory getObjectWrapperFactory() { return objectWrapperFactory; } public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) { this.objectWrapperFactory = objectWrapperFactory; } /** * Gets the interceptors. * * @return the interceptors * * @since 3.2.2 */ public List getInterceptors() { return interceptorChain.getInterceptors(); } public LanguageDriverRegistry getLanguageRegistry() { return languageRegistry; } public void setDefaultScriptingLanguage(Class driver) { if (driver == null) { driver = XMLLanguageDriver.class; } getLanguageRegistry().setDefaultDriverClass(driver); } public LanguageDriver getDefaultScriptingLanguageInstance() { return languageRegistry.getDefaultDriver(); } /** * Gets the language driver. * * @param langClass * the lang class * * @return the language driver * * @since 3.5.1 */ public LanguageDriver getLanguageDriver(Class langClass) { if (langClass == null) { return languageRegistry.getDefaultDriver(); } languageRegistry.register(langClass); return languageRegistry.getDriver(langClass); } /** * Gets the default scripting language instance. * * @return the default scripting language instance * * @deprecated Use {@link #getDefaultScriptingLanguageInstance()} */ @Deprecated public LanguageDriver getDefaultScriptingLanuageInstance() { return getDefaultScriptingLanguageInstance(); } public MetaObject newMetaObject(Object object) { return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory); } public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); return (ParameterHandler) interceptorChain.pluginAll(parameterHandler); } public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); } public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); return (StatementHandler) interceptorChain.pluginAll(statementHandler); } public Executor newExecutor(Transaction transaction) { return newExecutor(transaction, defaultExecutorType); } public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } return (Executor) interceptorChain.pluginAll(executor); } public void addKeyGenerator(String id, KeyGenerator keyGenerator) { keyGenerators.put(id, keyGenerator); } public Collection getKeyGeneratorNames() { return keyGenerators.keySet(); } public Collection getKeyGenerators() { return keyGenerators.values(); } public KeyGenerator getKeyGenerator(String id) { return keyGenerators.get(id); } public boolean hasKeyGenerator(String id) { return keyGenerators.containsKey(id); } public void addCache(Cache cache) { caches.put(cache.getId(), cache); } public Collection getCacheNames() { return caches.keySet(); } public Collection getCaches() { return caches.values(); } public Cache getCache(String id) { return caches.get(id); } public boolean hasCache(String id) { return caches.containsKey(id); } public void addResultMap(ResultMap rm) { resultMaps.put(rm.getId(), rm); checkLocallyForDiscriminatedNestedResultMaps(rm); checkGloballyForDiscriminatedNestedResultMaps(rm); } public Collection getResultMapNames() { return resultMaps.keySet(); } public Collection getResultMaps() { return resultMaps.values(); } public ResultMap getResultMap(String id) { return resultMaps.get(id); } public boolean hasResultMap(String id) { return resultMaps.containsKey(id); } public void addParameterMap(ParameterMap pm) { parameterMaps.put(pm.getId(), pm); } public Collection getParameterMapNames() { return parameterMaps.keySet(); } public Collection getParameterMaps() { return parameterMaps.values(); } public ParameterMap getParameterMap(String id) { return parameterMaps.get(id); } public boolean hasParameterMap(String id) { return parameterMaps.containsKey(id); } public void addMappedStatement(MappedStatement ms) { mappedStatements.put(ms.getId(), ms); } public Collection getMappedStatementNames() { buildAllStatements(); return mappedStatements.keySet(); } public Collection getMappedStatements() { buildAllStatements(); return mappedStatements.values(); } /** * @deprecated call {@link #parsePendingStatements(boolean)} */ @Deprecated public Collection getIncompleteStatements() { return incompleteStatements; } public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) { incompleteStatementsLock.lock(); try { incompleteStatements.add(incompleteStatement); } finally { incompleteStatementsLock.unlock(); } } /** * @deprecated call {@link #parsePendingCacheRefs(boolean)} */ @Deprecated public Collection getIncompleteCacheRefs() { return incompleteCacheRefs; } public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) { incompleteCacheRefsLock.lock(); try { incompleteCacheRefs.add(incompleteCacheRef); } finally { incompleteCacheRefsLock.unlock(); } } /** * @deprecated call {@link #parsePendingResultMaps(boolean)} */ @Deprecated public Collection getIncompleteResultMaps() { return incompleteResultMaps; } public void addIncompleteResultMap(ResultMapResolver resultMapResolver) { incompleteResultMapsLock.lock(); try { incompleteResultMaps.add(resultMapResolver); } finally { incompleteResultMapsLock.unlock(); } } public void addIncompleteMethod(MethodResolver builder) { incompleteMethodsLock.lock(); try { incompleteMethods.add(builder); } finally { incompleteMethodsLock.unlock(); } } /** * @deprecated call {@link #parsePendingMethods(boolean)} */ @Deprecated public Collection getIncompleteMethods() { return incompleteMethods; } public MappedStatement getMappedStatement(String id) { return this.getMappedStatement(id, true); } public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) { if (validateIncompleteStatements) { buildAllStatements(); } return mappedStatements.get(id); } public Map getSqlFragments() { return sqlFragments; } public void addInterceptor(Interceptor interceptor) { interceptorChain.addInterceptor(interceptor); } public void addMappers(String packageName, Class superType) { mapperRegistry.addMappers(packageName, superType); } public void addMappers(String packageName) { mapperRegistry.addMappers(packageName); } public void addMapper(Class type) { mapperRegistry.addMapper(type); } public T getMapper(Class type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } public boolean hasMapper(Class type) { return mapperRegistry.hasMapper(type); } public boolean hasStatement(String statementName) { return hasStatement(statementName, true); } public boolean hasStatement(String statementName, boolean validateIncompleteStatements) { if (validateIncompleteStatements) { buildAllStatements(); } return mappedStatements.containsKey(statementName); } public void addCacheRef(String namespace, String referencedNamespace) { cacheRefMap.put(namespace, referencedNamespace); } /* * Parses all the unprocessed statement nodes in the cache. It is recommended to call this method once all the mappers * are added as it provides fail-fast statement validation. */ protected void buildAllStatements() { parsePendingResultMaps(true); parsePendingCacheRefs(true); parsePendingStatements(true); parsePendingMethods(true); } public void parsePendingMethods(boolean reportUnresolved) { if (incompleteMethods.isEmpty()) { return; } incompleteMethodsLock.lock(); try { incompleteMethods.removeIf(x -> { x.resolve(); return true; }); } catch (IncompleteElementException e) { if (reportUnresolved) { throw e; } } finally { incompleteMethodsLock.unlock(); } } public void parsePendingStatements(boolean reportUnresolved) { if (incompleteStatements.isEmpty()) { return; } incompleteStatementsLock.lock(); try { incompleteStatements.removeIf(x -> { x.parseStatementNode(); return true; }); } catch (IncompleteElementException e) { if (reportUnresolved) { throw e; } } finally { incompleteStatementsLock.unlock(); } } public void parsePendingCacheRefs(boolean reportUnresolved) { if (incompleteCacheRefs.isEmpty()) { return; } incompleteCacheRefsLock.lock(); try { incompleteCacheRefs.removeIf(x -> x.resolveCacheRef() != null); } catch (IncompleteElementException e) { if (reportUnresolved) { throw e; } } finally { incompleteCacheRefsLock.unlock(); } } public void parsePendingResultMaps(boolean reportUnresolved) { if (incompleteResultMaps.isEmpty()) { return; } incompleteResultMapsLock.lock(); try { boolean resolved; IncompleteElementException ex = null; do { resolved = false; Iterator iterator = incompleteResultMaps.iterator(); while (iterator.hasNext()) { try { iterator.next().resolve(); iterator.remove(); resolved = true; } catch (IncompleteElementException e) { ex = e; } } } while (resolved); if (reportUnresolved && !incompleteResultMaps.isEmpty() && ex != null) { // At least one result map is unresolvable. throw ex; } } finally { incompleteResultMapsLock.unlock(); } } /** * Extracts namespace from fully qualified statement id. * * @param statementId * the statement id * * @return namespace or null when id does not contain period. */ protected String extractNamespace(String statementId) { int lastPeriod = statementId.lastIndexOf('.'); return lastPeriod > 0 ? statementId.substring(0, lastPeriod) : null; } // Slow but a one time cost. A better solution is welcome. protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) { if (rm.hasNestedResultMaps()) { final String resultMapId = rm.getId(); for (Object resultMapObject : resultMaps.values()) { if (resultMapObject instanceof ResultMap) { ResultMap entryResultMap = (ResultMap) resultMapObject; if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) { Collection discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap() .values(); if (discriminatedResultMapNames.contains(resultMapId)) { entryResultMap.forceNestedResultMaps(); } } } } } } // Slow but a one time cost. A better solution is welcome. protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) { if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) { for (String discriminatedResultMapName : rm.getDiscriminator().getDiscriminatorMap().values()) { if (hasResultMap(discriminatedResultMapName)) { ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName); if (discriminatedResultMap.hasNestedResultMaps()) { rm.forceNestedResultMaps(); break; } } } } } protected static class StrictMap extends ConcurrentHashMap { private static final long serialVersionUID = -4950446264854982944L; private final String name; private BiFunction conflictMessageProducer; public StrictMap(String name, int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); this.name = name; } public StrictMap(String name, int initialCapacity) { super(initialCapacity); this.name = name; } public StrictMap(String name) { this.name = name; } public StrictMap(String name, Map m) { super(m); this.name = name; } /** * Assign a function for producing a conflict error message when contains value with the same key. *

* function arguments are 1st is saved value and 2nd is target value. * * @param conflictMessageProducer * A function for producing a conflict error message * * @return a conflict error message * * @since 3.5.0 */ public StrictMap conflictMessageProducer(BiFunction conflictMessageProducer) { this.conflictMessageProducer = conflictMessageProducer; return this; } @Override @SuppressWarnings("unchecked") public V put(String key, V value) { if (containsKey(key)) { throw new IllegalArgumentException(name + " already contains key " + key + (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value))); } if (key.contains(".")) { final String shortKey = getShortName(key); if (super.get(shortKey) == null) { super.put(shortKey, value); } else { super.put(shortKey, (V) new Ambiguity(shortKey)); } } return super.put(key, value); } @Override public boolean containsKey(Object key) { if (key == null) { return false; } return super.get(key) != null; } @Override public V get(Object key) { V value = super.get(key); if (value == null) { throw new IllegalArgumentException(name + " does not contain value for " + key); } if (value instanceof Ambiguity) { throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name + " (try using the full name including the namespace, or rename one of the entries)"); } return value; } protected static class Ambiguity { private final String subject; public Ambiguity(String subject) { this.subject = subject; } public String getSubject() { return subject; } } private String getShortName(String key) { final String[] keyParts = key.split("\\."); return keyParts[keyParts.length - 1]; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy