本系列以Mybatis 3.3.X分支源码作为分析源,mybatis源码git地址:https://github.com/mybatis/mybatis-3.git。
mybatis的所有配置信息都存储到org.apache.ibatis.session.Configuration类中,可通过xml配置或者手动实例化获取到Configuration信息。
一、Configuration属性详解
Configuration类中属性定义主要有:Environment,Settings,Registry等
Environment描述:
Environment主要用于配置数据源和事务信息。Mybatis支持多环境设置,可以为开发,测试,生产使用不同的配置。
Environment的属性源码设置如下:
public final class Environment {
//当前环境的唯一标识
private final String id;
//指定事务工厂
private final TransactionFactory transactionFactory;
//指定数据库数据源
private final DataSource dataSource;
Settings描述:
Settings主要是一些运行时的全局设置,以下为Configuration中Settings可以配置的属性
//是否允许在嵌套语句中使用分页(RowBounds类主要包含offset和limit属性,用于分页使用)
protected boolean safeRowBoundsEnabled = false;
//是否允许在嵌套语句中使用分页(ResultHandler)
protected boolean safeResultHandlerEnabled = true;
//是否开启自动将数据库表字段映射为驼峰命名规则
protected boolean mapUnderscoreToCamelCase = false;
//该属性配置为true时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载。
protected boolean aggressiveLazyLoading = true;
//是否允许单一语句返回多结果集(需要兼容驱动)
protected boolean multipleResultSetsEnabled = true;
//允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)
protected boolean useGeneratedKeys = false;
//使用列标签代替列名
protected boolean useColumnLabel = true;
//缓存配置的全局开关
protected boolean cacheEnabled = true;
//指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。
protected boolean callSettersOnNulls = false;
//指定 MyBatis 增加到日志名称的前缀。
protected String logPrefix;
//指定 MyBatis 所用日志的具体实现,未指定时将自动查找
protected Class extends Log> logImpl;
//指定 VFS 的具体实现
protected Class extends VFS> vfsImpl;
//缓存作用域,分为LocalCacheScope.SESSION和LocalCacheScope.STATEMENT两种。
//SESSION表示会缓存一个会话中执行的所有查询。 STATEMENT表示本地会话仅用在语句执行上,对相同 SqlSession的不同调用将不会共享数据。
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
//当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
//指定哪些方法触发一次延迟加载,当使用了集合中的方法时,会触发数据加载
protected Set lazyLoadTriggerMethods = new HashSet(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
//设置JDBC的queryTimeout超时时间
protected Integer defaultStatementTimeout;
//设置JDBC的fetchSize属性,用于限制最大获取数据量,防止某些查询返回数据量太大导致OutOfMemory
protected Integer defaultFetchSize;
//配置默认的执行器。SIMPLE 就是普通的执行器;REUSE会重用预处理语句(prepared statements); BATCH将重用语句并执行批量更新
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
//指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
//延迟加载的全局开关
protected boolean lazyLoadingEnabled = false;
Registry描述:
Configuration中共定义了四个Registry,其中LanguageDriverRegistry驱动主要在Configuration构造函数中设置为XMLLanguageDriver, MapperRegistry用于自定义映射,下面详细描述TypeAliasRegistry和TypeHandlerRegistry:
1 TypeAliasRegistry typeAliasRegistry(别名注册器)
TypeAliasRegistry主要用来存储别名与JavaType类型的映射关系,内部使用了HashMap作为存储结构,并且在默认构造函数中注册了所有的基本类
TypeAliasRegistry也支持用户自定义别名,其加载自定义别名的具体实现方法详见org.apache.ibatis.builder.xml.XMLConfigBuilder中的typeAliasesElement方法:型(详见TypeAliasRegistry的构造函数实现), Configuration在其构造函数中同样注入了大量的全局别名信息,详见Configuration的默认构造器
//通过配置文件中加载出别名注册信息
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//如果配置为package,则将其注册为Object,此种情况下后续的解析会搜索此包下的所有JavaBean
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class> clazz = Resources.classForName(type);
if (alias == null) {
//如果没有配置别名,则优先查看是否有Alias注解,否则采用对应clazz的名称表示
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
TypeHandlerRegistry 主要用于保存JDBC Type,JAVA Type与处理器的映射关系,各个Handler主要继承BaseTypeHandler,用户可以继承自定义实现,框架默认提供的Handler详见org.apache.ibatis.type包下的实现。其中TypeHandlerRegistry默认注册了常用类型的处理器映射,详见TypehandlerRegistry的默认构造函数。
自定义TypeHanlder详见org.apache.ibatis.builder.xml.XMLConfigBuilder中的typeHandlerElement方法
其他配置描述:
Configuration中还有其他配置信息,大多为容器信息,等后续分析使用时再详细描述。如果需要了解更多Mybatis配置信息,请参见Mybatis XML配置
二、XML中加载配置文件
配置信息可以手动构造Configuration对象也可通过XML配置,因为默认的SqlSessionFactoryBuilder采用配置文件方式读取配置,所以在此分析XML加载相关的源码。SqlSessionFactory(后续文章会分析此类)为整个框架的入口点,其主要生成方式采用SqlSessionFactoryBuilder的build构建,build方法作为我们分析的起点。
SqlSessionFactoryBuilder类:
SqlSessionFactoryBuilder中共有许多build重载方法,参数共有如下几种:配置文件输入流(Reader、InputStream), Properties和Environment
完整的build方法如下所示,主要调用XMLConfigBuilder的parse方法生成Configuration对象,并返回DefaultSqlSessionoFactory实例:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
其中参数描述如下:
enviroment参数用于指定当前使用的环境ID,其对应配置文件中ID节点,如下所示的配置环境信息:
default="development">
id="development">
type="JDBC">
...
type="POOLED">
...
id="production">
type="MANAGED">
...
type="JNDI">
...
如果配置文件中不存在此ID的节点,则会使用default=“development”指定的默认环境
Properties用于指定属性配置信息,共有三种地方可以设置,build的构造函数传入理解为Java代码传入配置信息,下面会在分析源码的地方进行Properties加载顺序说明。
XMLConfigBuilder类:
XMLConfigBuilder的构造函数主要使用了XPathParser对象,XPathParser中的createDocument方法实现了数据流转为XML节点的功能,主要使用了jdk自带的xml解析库,不了解的可以去百度下。
进入XMLConfigBuilder的parse方法,我们发现其主要调用了parseConfiguration进行解析,parseConfiguration完成了所有XML节点与Configuraton实例的一一解析映射:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectionFactoryElement(root.evalNode("reflectionFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
以上代码中,参数root.evalNode中的参数名称即为XML节点的名称,均为根节点下的直属子节点,从名称可以看出节点的含义,可以第一节 Configuration配置详解中各个配置的描述,其中typeAliasesElement逻辑在第一节也分析过,其他类似。这里描述下propertiesElement方法中的逻辑,主要说明下Properties的加载顺序,详见mybatis官方文档描述: