---
title: 核心配置文件
author: 查尔斯
date: 2020/12/26 14:48
categories:
- MyBatis快速入门
tags:
- MyBatis
- ORM框架
---
# 核心配置文件
## 前言
**C:** 在上一篇,笔者带大家对 MyBatis 的核心对象做了介绍。本篇,笔者将继续带你学习 MyBatis,掌握对核心配置文件的使用。
MyBatis 的核心/全局配置文件 mybatis-config.xml ,顾名思义就是对 MyBatis 系统的核心设置文件。包含有 MyBatis 运行时行为配置、类型别名配置、环境配置等。
下方是核心配置文件的标签模板,笔者将对其中常用的一些标签的常用使用方式进行介绍。
configuration 根节点
- **properties** 属性配置
- settings 运行时行为配置
- **typeAliases** 类型别名配置
- typeHandlers 类型处理器
- objectFactory 对象工厂
- plugins 插件配置
- **environments** 环境配置
- environment 单个环境配置
- transactionManager 事务管理器配置
- dataSource 数据源配置
- databaseIdProvider 数据库厂商标识
- **mappers** 映射器配置
::: tip 笔者说
这些标签在使用时一定要注意标签的顺序和允许使用次数。Eclipse 中可以通过在标签上按 F2 查看该标签下的内容模型,即标签的顺序和允许使用次数。你看下图中画圈处就是各个标签的顺序,后面的 ?号 代表指定标签最多允许使用一次。
:::

## properties元素
如果你学过 Maven,那 properties 元素应该不难理解。在 MyBatis 的核心配置文件中,有很多配置是可能经常需要变动或复用的,如果直接将值硬编码在对应位置,将不利于统一维护管理和复用。
properties 元素的作用就体现出来了,它的使用方式有两种。
### 内部编写
**第一种使用方式,是内部编写配置** ,示例如下:
```xml
```
### 外部引入
**第二种使用方式,是在外部配置文件编写配置,然后通过 properties 引入外部配置** ,示例如下:
在 classpath 下 添加 一个 properties 配置文件,记录各种配置信息。(此处笔者记录的是数据源信息)
```
# MySQL
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mybatis_demo_db
mysql.username=root
mysql.password=root
```
```xml
```
::: tip 笔者说
大家猜一下,如果这两种方式在同时使用时遇到了相同配置,那么哪种方式的配置会生效呢?
测试思路: 可以先故意改错内部配置方式的 mysql.password 值,如果测试运行正常,说明外部配置生效了,反之则内部配置生效了。 也可以再故意调错外部的值试试。
:::
```xml
```
## settings元素(下篇讲解)
settings 元素是用来设置和改变 MyBatis 在运行时的一些行为的。
| **设置项** | **描述** | **允许值** | **默认值** |
| ------------------- | :----------------------------------------------------------: | :-------------------: | :--------: |
| cacheEnabled | 对在此配置文件下的所有cache进行全局性开/关设置 | true \| false | true |
| lazyLoadingEnabled | 全局性设置懒加载。如果设为false,则所有相关联的都会被初始化加载 | true \| false | true |
| autoMappingBehavior | MyBatis对于resultMap自动映射匹配级别 | NONE\|PARTIAL \|FULL | PARTIAL |
| **……(9个)** | **......** | **......** | **......** |
## typeAlias元素
在 SQL 映射文件中,我们在使用到某些类型时,需要编写好对应的全类名,大量的使用时,繁琐不说还容易错,如下 resultType 属性示例。
```xml
```
而typeAlias 元素就可以解决此问题,通过它的配置,可以为指定类型配置好别名,这样在 SQL 映射文件中就可以不用写全限定类名,而是直接使用配置的类型别名了。它的使用方式也有两种。
### 单个配置
**第一种使用方式:挨个对不同类型进行别名配置。**
```xml
```
### 包扫描
**第二种使用方式:当要配置别名的类型都在指定的 package 下时,可以直接开启包扫描,批量实现别名自动配置。**
```xml
```
### 使用效果
下方是有了类型别名配置之后,SQL 映射文件内使用类型的效果。
```xml
```
::: tip 笔者说
在 MyBatis 中有一个类 TypeAliasRegistry ,它的作用就是进行类型别名注册和解析,Java 中常见的类型都已经被它注册好了别名。
:::
```java
package org.apache.ibatis.type;
// ...略...
public class TypeAliasRegistry {
/** Map<类型别名, 对应类型的Class对象> */
private final Map> typeAliases = new HashMap<>();
/** 在创建对象时进行常用 Java 类型的别名注册 */
public TypeAliasRegistry() {
// String 类型注册的别名为 string
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);
registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);
registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);
registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);
registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);
registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);
registerAlias("ResultSet", ResultSet.class);
}
/**
* 解析别名
* @param string 要解析的别名
* @return 该别名对应的类型的Class对象
*/
public Class resolveAlias(String string) {
try {
if (string == null) {
return null;
}
// MyBatis 在【别名自动配置】和【解析映射文件中别名】时,对别名进行了小写转换。
// 所以在使用别名的时候才不区分大小写。
String key = string.toLowerCase(Locale.ENGLISH);
Class value;
if (typeAliases.containsKey(key)) {
value = (Class) typeAliases.get(key);
} else {
value = (Class) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
// ...略...
}
```
## environments元素
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
**不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。**
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:
- **每个数据库对应一个 SqlSessionFactory 实例** [1]
```xml
```
## mappers元素
mappers 元素的作用就是用来告诉 MyBatis 去哪找我们编写的 SQL 语句,它的使用方式有两大类。
### 指定映射文件
这类方式主要是告诉 MyBatis 我们所编写的 SQL 映射文件的地址,我们之前在 [《快速入门》](./01-快速入门) 中使用的就是属于这类方式。它有两种实现:
```xml
```
```xml
```
### 指定Mapper接口[重要]
这类方式还要涉及到 SqlSession 对象的另一个使用形式:**Mapper接口开发**
我们之前使用 SqlSession 是让它来直接执行指定的 SQL 语句。这种方式需要指明 SQL 映射文件 namespace的名字以及 SQL 语句的 id。因为是硬编码在代码中,维护时有诸多不便,例如:容易写错不说,还不利于我们在持久层采用面向接口编程思想。
```java
// 执行 SQL 语句
List userList = sqlSession.selectList("userMapper.selectList");
```
而 Mapper 接口开发就可以有效解决此问题,实现方式如下:
**第一步:先创建一个 Mapper 接口。** 前期就养成一个开发习惯,保持 Mapper 接口和对应 SQL 映射文件同名同包(虽然目前不同名也没事,但是先听话,养成习惯)。
```java
public interface UserMapper {
/**
* 查询用户列表
* @return
*/
List selectList();
}
```

**第二步:将 SQL 映射文件的 namespace 值改为对应 Mapper 接口的全限定类名。**
```xml
```
**第三步:将 SQL 映射文件中的 SQL 语句和 Mapper 接口中的方法进行绑定。**
```xml
```
**最后,我们在核心配置文件中,再配置好 Mapper 接口的全限定类名即可。**
```xml
```
**测试一下。**
```java
@Test
void testSelectList() throws IOException {
// 获取SqlSession对象
try (SqlSession sqlSession = MyBatisUtils.openSession()){
// 获取 Mapper 接口,而不再直接执行 SQL 语句
// 和以前 DAO 模式就非常相像了,只不过是 DAO 实现类变为了 SQL 映射文件
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 执行方法
List userList = userMapper.selectList();
// 遍历数据
userList.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
}
}
```
另外,在核心配置文件中,大量的配置 Mapper 接口全限定类名,还是有些麻烦,所以 MyBatis 在这也支持包扫描配置。
```xml
```
::: warning 笔者说
Mapper 接口开发是我们以后主要使用的方式,必须掌握!
:::
## 参考文献
[1]MyBatis 官网. MyBatis 配置[EB/OL]. https://mybatis.org/mybatis-3/zh/configuration.html. 2020-12-26
## 后记
::: info 笔者说
对于技术的学习,笔者一贯遵循的步骤是:先用最最简单的 demo 让它跑起来,然后学学它的最最常用 API 和 配置让自己能用起来,最后熟练使用的基础上,在空闲时尝试阅读它的源码让自己能够洞彻它的运行机制,部分问题出现的原因,同时借鉴这些技术实现来提升自己的代码高度。
所以在笔者的文章中,前期基本都是小白文,仅仅穿插很少量的源码研究。当然等小白文更新多了,你们还依然喜欢,后期会不定时专门对部分技术的源码进行解析。
:::