关注公众号 不爱总结的麦穗 将不定期推送技术好文
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
配置文档的顶层结构如下:
配置项
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
通过前文我们知道MyBatis初始化过程中会解析配置,那具体是如何解析的呢?我们接下来分析一下XMLConfigBuilder对象解析各个节点。
属性(properties)
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test1"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
properties节点设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。
- 解析properties节点——>propertiesElement(root.evalNode(“properties”))
private void propertiesElement(XNode context) throws Exception {
if (context == null) {
return;
}
// 将子节点的 name 以及value属性set进properties对象
Properties defaults = context.getChildrenAsProperties();
// 获取properties节点上 resource属性的值
String resource = context.getStringAttribute("resource");
// 获取properties节点上 url属性的值
String url = context.getStringAttribute("url");
// resource和url不能同时配置
if (resource != null && url != null) {
throw new BuilderException(
"The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
// 将configuration对象中已配置好的Properties值,放入Properties中
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
// set到解析器中对象中
parser.setVariables(defaults);
// set到configuration对象中
configuration.setVariables(defaults);
}
设置(settings)
<settings>
<!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
settings节点配置的值,会改变 MyBatis 的运行时行为。
- 解析settings节点——>settingsAsProperties(root.evalNode(“settings”))
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
// 将子节点的 name 以及value属性set进properties对象
Properties props = context.getChildrenAsProperties();
// 检查配置类是否已知所有设置
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException(
"The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
- configuration设置setting值——>settingsElement(settings)
private void settingsElement(Properties props) {
configuration
.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(
AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
/**
* 省略部分代码
*
**/
configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
configuration.setArgNameBasedConstructorAutoMapping(
booleanValueOf(props.getProperty("argNameBasedConstructorAutoMapping"), false));
configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
configuration.setNullableOnForEach(booleanValueOf(props.getProperty("nullableOnForEach"), false));
}
环境配置(environments)
<!-- 定义数据库的信息,默认使用development数据库构建环境 -->
<environments default="development">
<environment id="development">
<!-- 使用了 JDBC 的提交和回滚功能,它依赖从数据源获得的连接来管理事务作用域 -->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
environments节点可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置。通过配置environments的default属性就能选择对应的environment了
- 解析environments节点——>environmentsElement(root.evalNode(“environments”))
private void environmentsElement(XNode context) throws Exception {
if (context == null) {
return;
}
if (environment == null) {
// 获取default指定的环境
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
// environments 节点下可以拥有多个 environment子节点
// 获取environment的id
String id = child.getStringAttribute("id");
// 根据environments的default属性去选择对应的enviroment
if (isSpecifiedEnvironment(id)) {
// mybatis有两种事务:JDBC 和 MANAGED, JDBC是使用JDBC的事务,MANAGED是将事务托管给容器,
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 解析enviroment节点下的dataSource节点
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory)
.dataSource(dataSource);
// set到configuration对象中
configuration.setEnvironment(environmentBuilder.build());
break;
}
}
}
通过TransactionFactory类可以看到mybatis有两种事务:JDBC 和 MANAGED
插件(plugins)
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
MyBatis允许在映射语句执行过程中的某一点进行拦截调用,我们可以自定义插件来处理我们自己的逻辑。怎么自定义plugin ? 怎么配置?这篇文章就不细说了,后面会实战详细分析
- 解析plugins节点——>environmentsElement(root.evalNode(“environments”))
private void pluginsElement(XNode context) throws Exception {
if (context != null) {
for (XNode child : context.getChildren()) {
// 获取interceptor定义的插件
String interceptor = child.getStringAttribute("interceptor");
// 将子节点的 name 以及value属性set进properties对象
Properties properties = child.getChildrenAsProperties();
// 加载指定拦截器并创建实例
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor()
.newInstance();
interceptorInstance.setProperties(properties);
// 加入全局配置拦截器链
configuration.addInterceptor(interceptorInstance);
}
}
}
MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 执行sql语句
- ParameterHandler (getParameterObject, setParameters) 处理参数
- ResultSetHandler (handleResultSets, handleOutputParameters) 处理返回结果
- StatementHandler (prepare, parameterize, batch, update, query) Statement的处理器
映射器(mappers)
<!-- 定义映射器 -->
<mappers>
<mapper class="org.apache.ibatis.example.mapper.ScheduleSettingMapper"/>
</mappers>
mappers 节点用来配置我们的mapper映射文件,该节点主要就是告诉Mybatis去哪里找映射文件。mappers文件属于Mybatis的核心之一,我会在后续的文章单独做详解,这里也就不多说了。
总结
本文对 MyBatis 配置文件中部分配置的解析过程进行了详细的分析,主要是让大家知道大致是怎么解析的。可以看到解析配置节点都比较简单,有兴趣的小伙伴也可以尝试阅读一下其他节点的解析代码。