Mybatis配置文件解析
1、概述
-
Mybatis的核心配置文件:mybatis-config.xml,在本项目中配置文件路径为:src/main/resources/mybatis-config.xml
-
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息
-
在mybatis官方文档的显示中,能够配置的内容有如下几个方面
-
configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
需要注意上述所列出来的配置项的先后顺序,在配置文件中,若前后顺序不对,会报错
下面对几个重要且常见的配置项进行详解:
2、environments(环境配置)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置。不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
environments 元素定义了如何配置环境:
<!--配置中默认使用development环境-->
<environments default="development">
<!--第一套环境 development环境-->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--mysql8之后,添加时区设置-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="****"/>
<property name="password" value="****"/>
</dataSource>
</environment>
<!--配置的第二套环境 test环境-->
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--mysql8之后,添加时区设置-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="****"/>
<property name="password" value="****"/>
</dataSource>
</environment>
</environments>
注意点:
- 默认使用的环境 ID(比如:default=“development”)。
- 每个 environment 元素定义的环境 ID(比如:id=“development”)。
- 事务管理器的配置(比如:type=“JDBC”)。
- 数据源的配置(比如:type=“POOLED”)。
环境 ID 可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
1. 事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)
2. 数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
-
大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
-
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
- UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
- POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
- JNDI:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
3、映射器(mappers)
MapperRegistry:注册绑定所创建的mapper文件,告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。较为常用的绑定方式有两种
方法1: resource属性,层级路径之间用/分隔;
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="com/kevin/dao/UserMapper.xml"/>
<!--每一个mapper标签表示一个要绑定的mapper.xml-->
<mapper resource="com/kevin/dao/StudentMapper.xml"/>
</mappers>
方法2:使用Class文件绑定注册
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="com.kevin.dao.UserMapper"/>
<mapper class="com.kevin.dao.StudentMapper"/>
</mappers>
方法3:使用扫描包进行绑定,使用package标签直接引入mapper和mapper.xml所在的整个包
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="com.kevin.dao"/>
</mappers>
其中:方法2和方法3有相同的注意点:
- Mapper接口和对应的Mapper.xml配置文件必须具有相同的名字
- Mapper接口和其对应的Mapper.xml必须在同一个包下。
4、Properties优化
当在不同的项目需要使用不同的数据库时,如果用之前配置文件中固定的配置,则每次修改起来会十分不方便。可以通过Properties进行优化:
数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。优化步骤:
- 在资源目录resources下新建一个database.properties文件,文件中存储链接数据库的配置信息: 配置文件中=左右不能有空格,对应的配置信息的值无需用字符串显示;
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
# username配置自己的数据库用户名
username=****
# password配置自己的数据库密码即可
password=****
- 将database.properties文件导入到配置文件的properties 属性配置中
注意:properties属性配置在environments顺序之前,即每个配置标签都有自己固定的顺序位置,位置出错,会报错
导入properties文件之后,environments标签中的datasource中的每个属性,可直接通过${键名}直接获取(类似于mapper.xml中sql语句获取参数)
${}中的字段名需要与properties文件中的=左侧一致
<configuration>
<!--导入properties文件-->
<properties resource="database.properties"/>
</properties>
<environments default="development">
<environment id="development">
<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>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
注意点:如果在properties配置了username和password,并且在properties文件中也有相同的username和password的参数,但是对应的值不一样,则以properties文件中的配置为准,properties文件中的配置信息优先级高于properties标签中的信息。
若properties文件中没有配置username和password的信息,则以properties标签中的配置信息为准
<properties resource="database.properties">
<!---->
<property name="username" value="root"/>
<property name="password" value="123321"/>
</properties>
5、typeAliases(起别名)
创建的mapper.xml中的增删改查语句的resultType每次都写成"com.kevin.dao.User"这种全类名形式,偶尔几次还行,在一个项目中可能需要成百上千次的设置resultType的类型,有必要给它起个别名,类似于sql语句中对表,字段起别名。一句话讲:类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
方法:在配置文件中添加typeAliases标签:
<!--配置别名,注意顺序-->
<typeAliases>
<typeAlias type="com.kevin.pojo.User" alias="User"/>
</typeAliases>
当进行上述配置时,可以使用User代替"com.kevin.pojo.User"进行使用。
然而,当需要的实体类较多时,分别对每个类起别名也是一个较大的工程呢,因此,也可以指定一个包名,Mybatis会在包名下面搜索需要的 Java Bean,并默认为该包下的所有类起别名,别名为非限定类名,首字母小写。比如:
<typeAliases>
<package name="com.kevin.pojo"/>
</typeAliases>
对比:
-
对类起别名:起别名不受限制,可以进行自定义任何别名;
-
对整个包中的类起别名:默认别名为类名的首字母大写,不能随意自定义;当然,如果想要进行自定义,也可以通过在该类上加注解@Alias(“自定义名称”)完成自定义起别名
// 给该类起别名为diyUser
@Alias("diyUser")
public class User {
private String name;
private int age;
private String sex;
}
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格:
基本数据类型起别名:在前面加下划线;引用数据类型首字母小写
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
6、其他配置
-
设置(settings)相关,详细 查看Mybatis帮助文档
-
- 懒加载
- 日志实现
- 缓存开启关闭
-
一个配置完整的 settings 配置的示例如下:
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
类型处理器
- 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
- 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】
对象工厂
- MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
- 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。
- 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。【了解即可】
7、作用域和生命周期
解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。
Mybatis的执行过程如下:
作用域理解:
- SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,一旦创建了 SqlSessionFactory,就不再需要它了,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
- SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中, 一旦SqlSessionFactory被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。
- 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。
- 如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。所以 SqlSession 的最佳的作用域是请求或方法作用域。