Configuration类是mybatis核心配置类,无论是全局配置文件还是mapping配置文件亦或是注解,所有的配置最终都会维护Configuration中,所以解读Configuration类,是学习mybatis的重中之重。
本文不会和其他博客一样列出xml配置文件,然后在每一个属性上标上注释,而是采取在源码上编写注释来达到学习的目的,一来,这样更加直观,记忆更深刻。二来,去XML配置化,全面拥抱注解是已经成为当今主流的开发方式,因此本人侧重点也是注解,而非xml。
除了把mybatis官网上列出的属性一一标注到属性上以外,还增加了一些自己的理解,有些官网没有给出说明的方法,我也试着去理解然后标上注释,下面放出我的学习成果,有理解不对的,错误的,还请各位指正。
mybatis 版本:3.5.8-SNAPSHOT
/**
* @author Clinton Begin
*/
public class Configuration {
protected Environment environment;
/**
* 是否允许在嵌套语句中使用分页,true:不允许,false:允许
* */
protected boolean safeRowBoundsEnabled;
/**
* 是否允许在嵌套语句中使用结果处理器(ResultHandler),true:不允许,false:允许
* default:true
* */
protected boolean safeResultHandlerEnabled = true;
/**
* 是否开启驼峰命名自动映射,e.g:USER_NAME ->userName ,true:不允许,false:允许
* */
protected boolean mapUnderscoreToCamelCase;
/**
*开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载
* */
protected boolean aggressiveLazyLoading;
/**
* 是否允许单个语句返回多结果集(需要数据库驱动支持)。
* */
protected boolean multipleResultSetsEnabled = true;
/**
* 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。
* 尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。
* default:false
* */
protected boolean useGeneratedKeys;
/**
* 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。
* */
protected boolean useColumnLabel = true;
/**
* 如果映射文件中有配置缓存标签,则开启,默认开启,但是配置文件中不配置cache标签,该属性不生效
* */
protected boolean cacheEnabled = true;
/**
*指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null
* 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。
* default:false
**/
protected boolean callSettersOnNulls;
/**
* 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。
* (新增于 3.4.1)
* */
protected boolean useActualParamName = true;
/**
* 当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,
* 它也适用于嵌套的结果集(如集合或关联)(新增于 3.4.2)
* default:false
* */
protected boolean returnInstanceForEmptyRow;
/**
* 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5)
* default:false
* */
protected boolean shrinkWhitespacesInSql;
/**
*指定 MyBatis 增加到日志名称的前缀。
* **/
protected String logPrefix;
/**
* 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
* 可以是:SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
* **/
protected Class<? extends Log> logImpl;
/**
* 指定 VFS 的实现,自定义 VFS 的实现的类全限定名,以逗号分隔。
* */
protected Class<? extends VFS> vfsImpl;
/**
* Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value)
* attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted.
*
* 为@SelectProvider|@InsertProvider| @UpdateProvider|DeleteProvider提供一个全局的SQL Provider,假设某一个方法上注释了
* @SelectProvider,而未配置SQLProvider,如果配置了该属性,则使用该属性配置的SQLProvider,具体参考测试用例
* SQLProviderTest.omitTypeWhenSpecifyDefaultType方法
* 如果方法上加了上述四个注解,但是没有type和value属性,则取属性,如果该属性也没配置,则抛出异常
* throw new BuilderException("Please specify either 'value' or 'type' attribute of @"
* + providerAnnotation.annotationType().getSimpleName()
* + " at the '" + mapperMethod.toString() + "'.");
*
* **/
protected Class<?> defaultSqlProviderType;
/**
* MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。
* 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。
* sqlSession作用域mybatis会话周期
* statement作用域 jdbc statement会话周期
* default:session
* */
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
/**
*当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,
* 比如 NULL、VARCHAR 或 OTHER。
* 如果插入空值有类似异常,Error setting null for parameter #2 with JdbcType OTHER . 可尝试将该值设置为NULL
* default:OTHER
* **/
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
/**
* 指定对象的哪些方法触发一次延迟加载,用逗号分隔的方法列表。
* 具体用法请参照LazyPropertiesTest.verifyCustomLazyLoadTriggerMethods()方法
* default:equals,clone,hashCode,toString
* **/
protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
/**
* 设置超时时间,它决定数据库驱动等待数据库响应的秒数。
* */
protected Integer defaultStatementTimeout;
/**
*设置从结果集中抓取数据的数量,JDBC默认抓取量是10条,适当调大该阈值可以提升性能
* **/
protected Integer defaultFetchSize;
/**
*指定语句默认的滚动策略。(新增于 3.5.2)
* **/
protected ResultSetType defaultResultSetType;
/**
* 配置默认的执行器。
* SIMPLE 就是普通的执行器;
* REUSE 执行器会重用预处理语句(PreparedStatement);
* BATCH 执行器不仅重用语句还会执行批量更新。
* default+SIMPLE
* **/
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
/**
* 指定 MyBatis 应如何自动映射列到字段或属性。
* NONE 表示关闭自动映射;
* PARTIAL 只会自动映射没有定义嵌套结果映射的字段。
* FULL 会自动映射任何复杂的结果集(无论是否嵌套)。
* default:PARTIAL
* */
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
/**
* 指定发现自动映射目标未知列(或未知属性类型)的行为。
* NONE: 不做任何反应
* WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)
* FAILING: 映射失败 (抛出 SqlSessionException)
* default:NONE
* **/
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
/**
*
* */
protected Properties variables = new Properties();
/**
* 反射工厂,reflector对象对反射API进行了二次的封装,但是它的功能仅限于收集类的元信息,例如这个类的getter,setter等等,
* 这些元信息组成了一个个的reflector对象,所以它虽然是反射API,但它只用于生产reflector对象,真正生产对象的是ObjectFactory,
* 莫要搞混了
* @See DefaultReflectorFactory
* */
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
/**
* 对象工厂,
* @See DefaultObjectFactory
* MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,
* 要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。
* */
protected ObjectFactory objectFactory = new DefaultObjectFactory();
/**
* 对象包装器工厂,官方提供的默认实现不支持此功能
* **/
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
/**
*延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。
* default:false
* **/
protected boolean lazyLoadingEnabled = false;
/**
* 代理工厂,用于生产mapper的实现类
* default JavassistProxyFactory
* **/
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
/**
* MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
* */
protected String databaseId;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.*
* @see <a href='https://github.com/mybatis/old-google-code-issues/issues/300'>Issue 300 (google code)</a>
* 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。
* 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)
*/
protected Class<?> configurationFactory;
/**
* mapper注册器
* mapper注册器注册的是MapperProxyFactory对象,不是mapper的实现类
* */
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
/**
* 拦截器链,用于执行插件逻辑
* **/
protected final InterceptorChain interceptorChain = new InterceptorChain();
/**
* 类型处理器注册器
* 类型处理:jdbc type和java type互转
**/
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
/**
* java类型别名注册器
* e.g string -> String.class
* */
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
/**
* 配置解析驱动器注册器
* **/
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
/**
*存储MappedStatement对象,key为 map的Id
* StrictMap本质上还是hashmap,多了name和conflictMessageProducer
* name是标识该集合对象缓存的是哪类数据
** conflictMessageProducer是key冲突以后抛出IllegalArgumentException异常,异常提示可以定制化,即name+conflictMessageProducer
* 比如mappedStatement,假设已经存在id为getUserIyId的mappedStatement的对象,这时再次调用put方法添加该对象
* 此时put方法内部(strictMap的put方法覆盖了hashmap的put方法)会检查该mappedStatement是否存在,如果存在则抛异常,异常提示信息为
* Mapped Statements collection already contains value for getUserById , please check... and ...
* */
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
/**
*缓存
* */
protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
/**
* 结果集,
* */
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
/**
* 参数集
* **/
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
/**
* 主键生成策略集
* */
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
protected final Set<String> loadedResources = new HashSet<>();
protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();
/*
* 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<String, String> 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<? extends Log> getLogImpl() {
return logImpl;
}
public void setLogImpl(Class<? extends Log> logImpl) {
if (logImpl != null) {
this.logImpl = logImpl;
LogFactory.useCustomLogging(this.logImpl);
}
}
public Class<? extends VFS> getVfsImpl() {
return this.vfsImpl;
}
public void setVfsImpl(Class<? extends VFS> 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;
}
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<String> getLazyLoadTriggerMethods() {
return lazyLoadTriggerMethods;
}
public void setLazyLoadTriggerMethods(Set<String> 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<? extends TypeHandler> 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<Interceptor> getInterceptors() {
return interceptorChain.getInterceptors();
}
public LanguageDriverRegistry getLanguageRegistry() {
return languageRegistry;
}
public void setDefaultScriptingLanguage(Class<? extends LanguageDriver> 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<? extends LanguageDriver> 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);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return 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);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
/*
*statement处理器
* **/
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);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
/**
* 根据给定的事务和默认执行器(SIMPLE)类型创建一个执行器,可通过配置文件/code修改默认配置
* 执行器时真正和数据库交互的角色,它帮忙我们完成增删查改,mybatis提供了三种执行器:
* SIMPLE 就是普通的执行器;
* REUSE 执行器会重用预处理语句(PreparedStatement);
* BATCH 执行器不仅重用语句还会执行批量更新。
* **/
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
/**
* 根据给定的事务和执行器类型创建一个执行器
* **/
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : 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);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
keyGenerators.put(id, keyGenerator);
}
public Collection<String> getKeyGeneratorNames() {
return keyGenerators.keySet();
}
public Collection<KeyGenerator> 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<String> getCacheNames() {
return caches.keySet();
}
public Collection<Cache> 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<String> getResultMapNames() {
return resultMaps.keySet();
}
public Collection<ResultMap> 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<String> getParameterMapNames() {
return parameterMaps.keySet();
}
public Collection<ParameterMap> 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<String> getMappedStatementNames() {
buildAllStatements();
return mappedStatements.keySet();
}
public Collection<MappedStatement> getMappedStatements() {
buildAllStatements();
return mappedStatements.values();
}
public Collection<XMLStatementBuilder> getIncompleteStatements() {
return incompleteStatements;
}
public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
incompleteStatements.add(incompleteStatement);
}
public Collection<CacheRefResolver> getIncompleteCacheRefs() {
return incompleteCacheRefs;
}
public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
incompleteCacheRefs.add(incompleteCacheRef);
}
public Collection<ResultMapResolver> getIncompleteResultMaps() {
return incompleteResultMaps;
}
public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
incompleteResultMaps.add(resultMapResolver);
}
public void addIncompleteMethod(MethodResolver builder) {
incompleteMethods.add(builder);
}
public Collection<MethodResolver> 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<String, XNode> 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 <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
public <T> T getMapper(Class<T> 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();
if (!incompleteCacheRefs.isEmpty()) {
synchronized (incompleteCacheRefs) {
incompleteCacheRefs.removeIf(x -> x.resolveCacheRef() != null);
}
}
if (!incompleteStatements.isEmpty()) {
synchronized (incompleteStatements) {
incompleteStatements.removeIf(x -> {
x.parseStatementNode();
return true;
});
}
}
if (!incompleteMethods.isEmpty()) {
synchronized (incompleteMethods) {
incompleteMethods.removeIf(x -> {
x.resolve();
return true;
});
}
}
}
private void parsePendingResultMaps() {
if (incompleteResultMaps.isEmpty()) {
return;
}
synchronized (incompleteResultMaps) {
boolean resolved;
IncompleteElementException ex = null;
do {
resolved = false;
Iterator<ResultMapResolver> iterator = incompleteResultMaps.iterator();
while (iterator.hasNext()) {
try {
iterator.next().resolve();
iterator.remove();
resolved = true;
} catch (IncompleteElementException e) {
ex = e;
}
}
} while (resolved);
if (!incompleteResultMaps.isEmpty() && ex != null) {
// At least one result map is unresolvable.
throw ex;
}
}
}
/**
* Extracts namespace from fully qualified statement id.
*
* @param statementId
* the statement id
* @return namespace or null when id does not contain period.
* 抽取 statementId中的命名空间
* e.g
* 对于 com.example.mybatis.demo.selectUserById 返回 com.example.mybatis.demo
* 对于 selectUserById, 返回 null
*/
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()) {
for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
Object value = entry.getValue();
if (value instanceof ResultMap) {
ResultMap entryResultMap = (ResultMap) value;
if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {
Collection<String> discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap().values();
if (discriminatedResultMapNames.contains(rm.getId())) {
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 (Map.Entry<String, String> entry : rm.getDiscriminator().getDiscriminatorMap().entrySet()) {
String discriminatedResultMapName = entry.getValue();
if (hasResultMap(discriminatedResultMapName)) {
ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);
if (discriminatedResultMap.hasNestedResultMaps()) {
rm.forceNestedResultMaps();
break;
}
}
}
}
}
/**
* 对HashMap的二次封装
* 使用该map必须提供一个name属性来标识该map存放的数据类型
* 同时提供了conflictMessageProducer对象来定制化错误提醒
* */
protected static class StrictMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
private BiFunction<V, V, String> 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) {
super();
this.name = name;
}
public StrictMap(String name, Map<String, ? extends V> m) {
super(m);
this.name = name;
}
/**
* Assign a function for producing a conflict error message when contains value with the same key.
* <p>
* 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
* 参见 jdk1.8 函数式编程接口 function
*/
public StrictMap<V> conflictMessageProducer(BiFunction<V, V, String> conflictMessageProducer) {
this.conflictMessageProducer = conflictMessageProducer;
return this;
}
@Override
@SuppressWarnings("unchecked")
/**
* 可定制提示信息,明确是哪个类型下的哪个key重复了
* com.example.mybatis.demo.selectUserById,对于这个key,截取 selectUserById做为key保存,如果已存在selectUserById,
* 则包装为Ambiguity,在get的时候报错
* 其实就是为了允许。命名空间+key的形式存在,同时防止重复,至于为什么要截取,目前还不清楚
* */
public V put(String key, V value) {
if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + 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
/**
* 重写 map的put方法
* 如果key不存在,则抛出异常
* 如果key存在,但value是Ambiguity(歧义)也抛出异常
* 更严格的get,没获取到key
* **/
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;
}
/**
* 如果存入 strictMap中的key 中包含 ".",会取最后一个“.”后面的字符
* e.g com.example.mybatis.demo.selectUserById
* 那么取到的key就是selectUserById
* 如果 strictMap中没有此key,则直接存入,
* 如果strictMap中已经存在此key,strictMap不会放弃存储,会将该key封装为Ambiguity(歧义)保存起来,不会丢弃,也不会提示
* 当调用该方法时如果获取到的value是Ambiguity类型则会跑出异常
*
* e.g
* 1. 存入 selectUserById
* 2. 存入com.example.mybatis.demo.selectUserById
* 3. 调用 get(selectUserById) 则抛出异常并提示
* selectUserById is ambiguous in mappedstatement collection ,((try using the full name including the namespace,
* or rename one of the entries)
*
* 像是是个容错机制
* **/
protected static class Ambiguity {
private final String subject;
public Ambiguity(String subject) {
this.subject = subject;
}
public String getSubject() {
return subject;
}
}
/**
* 根据‘.’来切割字符串,并返回切割后的数组的最后一个元素
* e.g com.example.mybatis.demo.selectUserById -> selectUserById
* **/
private String getShortName(String key) {
final String[] keyParts = key.split("\\.");
return keyParts[keyParts.length - 1];
}
}
}
本文详细解读了MyBatis的核心配置类`Configuration`,包括其属性和方法,如环境配置、自动映射策略、缓存设置、执行器类型等。通过对源码的注释,帮助读者理解MyBatis的工作原理,特别是注解开发模式下的关键配置。此外,还介绍了如何通过`Configuration`类进行SQL提供者、对象工厂、缓存等的定制化设置。
1万+

被折叠的 条评论
为什么被折叠?



