1.全局配置文件
全局配置文件,见名知意就是对当前MyBatis的一些全局属性进行设置。也就是对各种数据操作进行统一规定。
全局配置文件包含了全局设置(setting)和properties两个大的部分,通过这两个大的部分动态的影响MyBatis的行为。下面我从几个方面去介绍:
大致来看看最简单的全局配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
...
<typeAliases>
<environments default="development">
<mappers>
...
</configuration>
但是,上面各个标签的顺序,我绝对不是随便给的。MyBatis对各个标签的顺序有规定。
如果标签的顺序不正确,会出现错误,如下图:
核心配置文件中的标签必须按照固定的顺序(有的标签可以不写,但顺序一定不能乱):
properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers
那我就按照上述的顺序来将一些常见的进行介绍:
2、properties: 通过配置文件动态绑定值
表名的是可外部化,可替换的一些属性。在典型的JAVA属性中配置他们文件的实例。比如jdbc.properties文件的导入。
[jdbc.properties]
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3307/mybatis
jdbc.username=root
jdbc.password=admin
然后,我们就可以在数据源中动态的设置这些值。
<environment id="test">
<!--事务管理器通过jdbc来进行管理-->
<transactionManager type="JDBC"/>
<!--数据源,从数据连接池中进行取用 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
2. setting :对全局设置相关属性值
是在mybatis运行过程中修改运行行为一个非常重要的方式。其中规定了很多属性的值,如是否开启全局缓存,懒加载是否开启等等.
我只截取其中的一部分,其他详细的请查阅相关文档。
2.1 cacheEnabled
结论:cachedEnable这个开关实际上控制的上创建executor(用来处理sql的)的类型,创建的executor取决于defaultExecutorType默认是SimpleExecutor这个类是没有二级缓存功能的,如果cachedEnable是true(默认),就会使用CachingExecutor对SimpleExecutor包装。CachingExecutor这个是具有二级缓存功能的。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// 获得执行器类型
executorType = executorType == null ? defaultExecutorType : executorType; // 使用默认
executorType = executorType == null ? ExecutorType.SIMPLE : executorType; // 使用 ExecutorType.SIMPLE
// 创建对应实现的 Executor 对象
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);
}
// 如果开启缓存,创建 CachingExecutor 对象,进行包装
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 应用插件
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
所以cachedEnable=false二级缓存就被关闭了,但是cachedEnable=true仅仅是使用了CachingExecutor,不能代表使用了二级缓存,因为CachingExecutor使用了Cache对象缓存数据,只有在Mapper中配置标签,解析的时候才会有Cache对象,否则还是不会用到二级缓存的。
(二级坑比较多,用的人也很少)
2.2 启用延迟加载和按需加载
懒加载针对什么使用的?为什么要用懒加载?
懒加载针对级联使用的,懒加载的目的是减少内存的浪费和减轻系统负担。
懒加载是什么?
你可以理解为按需加载,当我调用到关联的数据时才与数据库交互否则不交互。
再具体点来说:
比如user表和role表有关联关系,有这样一条语句:查询uesr的同时将user的某一列数据作为参数一并查询role表符合条件的数据,mybatis里叫做级联。只要执行这条语句,就会将这两张表符合需求的信息一起加载出来。而懒加载只会加载uesr表的数据出来不加载role表的数据。
还不懂怎么办?
将下面的代码敲一遍,不要做任何的调用只运行下面的方法:
public void testFindAll(){
users = userDao.findAll();
}
至于为什么下面你将会知道。
如何使用开启懒加载?
在mybatis的主配置文件的Setings标签开启。
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
注意:标签的顺序,必须位于properties之后typeAliases之前。如果位置错乱会导致mybatis启动失败。
注意:这个是开启全局的懒加载开关。
lazyLoadingEnabled默认为false。
aggressiveLazyLoading 3.4.1(包含)前为true,之后为false。
开启懒加载一定要两个属性都配置吗?
不是的,真正开启懒加载标签的只是lazyLoadingEnabled:true表示开启,false表示关闭,默认为false。
如果我不想将全部的级联都设计懒加载怎么办?
association和collection有个fetchType属性可以覆盖全局的懒加载状态:eager表示这个级联不使用懒加载要立即加载,lazy表示使用懒加载。
注意:discriminator没有fetchType属性,因此它只能随大众不能有自己的小九九。
鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为 封装Employee
如果查出的是女生 : 就把部门信息查询出来,否则不查询
如果是男生 : 把username这一列的值赋值给email
<resultMap type="cn.yuanyu.mybatis.entity.Employee" id="discriminatorResultMap">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!--
column : 指定判定的列名
javaType : 列值对应的java类型
-->
<discriminator javaType="string" column="gender">
<!--
女生
resultType : 指定封装的结果类型,不能缺少-->
<case value="女" resultType="cn.yuanyu.mybatis.entity.Employee">
<association property="dept"
select="cn.yuanyu.mybatis.mapper.DepartmentMapper.getDeptById"
column="d_id">
</association>
</case>
<!--
男生
如果是男生,把username这一列的值赋值给email
-->
<case value="男" resultType="cn.yuanyu.mybatis.entity.Employee">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="username" property="email"/>
<result column="gender" property="gender"/>
</case>
</discriminator>
</resultMap>
<select id="discriminator" resultMap="discriminatorResultMap">
select *
from t_employee
where id = #{id}
</select>
既然fetchType可以控制懒加载那么我仅仅配置fetchType不配置全局的可以吗?
可以的。
aggressiveLazyLoading是做什么么的?
它是控制具有懒加载特性的对象的属性的加载情况的。
true表示如果对具有懒加载特性的对象的任意调用会导致这个对象的完整加载,false表示每种属性按照需要加载。
还是不懂怎么办?
看具体效果:
两个接口:
public interface IRole {
List<Role> findAll();
List<Role> findById(String id);
}
public interface IUser {
List<User> findAll();
}
两个实体类:
public class Role implements Serializable {
private String id;
private String userId;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
@Override
public String toString() {
return "Role{" +
"id='" + id + '\'' +
", userId='" + userId + '\'' +
'}';
}
}
public class User implements Serializable {
private String id;
private List<Role> role;
private String name ;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
name = "丽华";
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Role> getRole() {
return role;
}
public void setRole(List<Role> role) {
this.role = role;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
'}';
}
}
三个配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置延迟加载-->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
<!-- 包下的实体类以小写类名为别名-->
<typeAliases>
<package name="com.itheima.domain"/>
</typeAliases>
<!--配置环境-->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务 -->
<transactionManager type="JDBC"/>
<!--配置连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///test?serverTimezone=UTC"/>
<property name="username" value="rooot"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<!-- 配置映射文件的位置 -->
<mappers>
<package name="com.itheima.dao"/>
</mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespace:命名空间,其值为某一个dao层类的具体路径
-->
<mapper namespace="com.itheima.dao.IRole">
<resultMap id="resultMap" type="com.itheima.domain.Role">
<id property="id" column="id"/>
<result property="userId" column="user_id"/>
</resultMap>
<select id="findById" resultMap="resultMap" parameterType="string">
select * from role where user_id=#{id}
</select>
<select id="findAll" resultMap="resultMap">
select * from role
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespace:命名空间,其值为某一个dao层类的具体路径
-->
<mapper namespace="com.itheima.dao.IUser">
<resultMap id="resultMap" type="com.itheima.domain.User">
<id property="id" column="id"/>
<collection property="role" column="id" select="com.itheima.dao.IRole.findById"/>
</resultMap>
<select id="findAll" resultMap="resultMap">
select * from user
</select>
</mapper>
两个表:
role表:
这里简单描述一下;
uesr和role的关系为一对多。开启了全局懒加载。
并且在user的配置中配置级联,那么称user的实例为带有懒加载属性的对象。
aggressiveLazyLoading的属性为false,即每种属性按需加载,不调用就不加载。
运行测试方法:
@Test
public void testFindAll(){
users = userDao.findAll();
for(User user : users){
System.out.println("-----每个用户的信息------");
System.out.println(user.getName());
}
}
结果:
2019-11-26 19:37:21,627 128 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Opening JDBC Connection
2019-11-26 19:37:22,323 824 [ main] DEBUG source.pooled.PooledDataSource - Created connection 793138072.
2019-11-26 19:37:22,326 827 [ main] DEBUG com.itheima.dao.IUser.findAll - ==> Preparing: select * from user
2019-11-26 19:37:22,347 848 [ main] DEBUG com.itheima.dao.IUser.findAll - ==> Parameters:
2019-11-26 19:37:22,400 901 [ main] DEBUG com.itheima.dao.IUser.findAll - <== Total: 4
-----每个用户的信息------
丽华
-----每个用户的信息------
丽华
-----每个用户的信息------
丽华
-----每个用户的信息------
丽华
2019-11-26 19:37:22,401 902 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2f465398]
2019-11-26 19:37:22,401 902 [ main] DEBUG source.pooled.PooledDataSource - Returned connection 793138072 to pool.
可以看到只执行user的sql语句,并且只加载调用的属性并没有加载 private List role;
下面将aggressiveLazyLoading设置为true:只要对这个类的任意操作将完整加载整个类的所有属性即执行级联的SQL语句
运行同样的测试方法:
2019-11-26 19:35:33,893 123 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Opening JDBC Connection
2019-11-26 19:35:34,597 827 [ main] DEBUG source.pooled.PooledDataSource - Created connection 793138072.
2019-11-26 19:35:34,600 830 [ main] DEBUG com.itheima.dao.IUser.findAll - ==> Preparing: select * from user
2019-11-26 19:35:34,621 851 [ main] DEBUG com.itheima.dao.IUser.findAll - ==> Parameters:
2019-11-26 19:35:34,677 907 [ main] DEBUG com.itheima.dao.IUser.findAll - <== Total: 4
-----每个用户的信息------
2019-11-26 19:35:34,678 908 [ main] DEBUG com.itheima.dao.IRole.findById - ==> Preparing: select * from role where user_id=?
2019-11-26 19:35:34,679 909 [ main] DEBUG com.itheima.dao.IRole.findById - ==> Parameters: 1(String)
2019-11-26 19:35:34,680 910 [ main] DEBUG com.itheima.dao.IRole.findById - <== Total: 2
丽华
-----每个用户的信息------
2019-11-26 19:35:34,680 910 [ main] DEBUG com.itheima.dao.IRole.findById - ==> Preparing: select * from role where user_id=?
2019-11-26 19:35:34,680 910 [ main] DEBUG com.itheima.dao.IRole.findById - ==> Parameters: 2(String)
2019-11-26 19:35:34,681 911 [ main] DEBUG com.itheima.dao.IRole.findById - <== Total: 2
丽华
-----每个用户的信息------
2019-11-26 19:35:34,681 911 [ main] DEBUG com.itheima.dao.IRole.findById - ==> Preparing: select * from role where user_id=?
2019-11-26 19:35:34,681 911 [ main] DEBUG com.itheima.dao.IRole.findById - ==> Parameters: 4(String)
2019-11-26 19:35:34,682 912 [ main] DEBUG com.itheima.dao.IRole.findById - <== Total: 0
丽华
-----每个用户的信息------
2019-11-26 19:35:34,682 912 [ main] DEBUG com.itheima.dao.IRole.findById - ==> Preparing: select * from role where user_id=?
2019-11-26 19:35:34,682 912 [ main] DEBUG com.itheima.dao.IRole.findById - ==> Parameters: 5(String)
2019-11-26 19:35:34,683 913 [ main] DEBUG com.itheima.dao.IRole.findById - <== Total: 3
丽华
2019-11-26 19:35:34,684 914 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2f465398]
2019-11-26 19:35:34,684 914 [ main] DEBUG source.pooled.PooledDataSource - Returned connection 793138072 to pool.
我们只调用了getName方法没有调用getRole方法但mybatis却调用了查询role的语句。
现在可以更明确aggressiveLazyLoading的作用了:
true:
只要调用任意具有懒加载特性的对象的任意一个属性将完整加载整个对象,即触发级联效果。
false:
只加载调用的属性,不调用的属性不加载。
还是不懂怎么办?
将上面的过程自己来一遍。
还有一个问题,如果我仅仅使用aggressiveLazyLoading不使用lazyLoadingEnabled还有能按需加载吗?即调用getName不执行role的sql语句,就像第一种情况那样
如果不开启懒加载,在执行user的sql语句时也将执行role的sql语句。因此不能实现需求。
2.3 multipleResultSetsEnabled
一条sql 对应多个结果集,resultMap 多个
2.4、useColumnLabel
确切的说当映射找不到参数时会使用列标签(数据库列名)代替别名去映射
3. typeAliases( alias,package) 设置类型的别名
设置别名:为一些比较长的类名起一个比较短的代称。
里面有的属性有
type: 需要设置别名的全类型名。
alias: 设置此类型的别名,并且不进行区分大小写 ,如果这项不进行设定的话,框架给一个默认的别名——以类名作为别名。
<typeAliases>
<typeAlias type="com.fangshaolei.mybatis.Account" alias="account" />
<typeAlias type="com.fangshaolei.mybatis.User" alias="uu" />
</typeAliases>
但是,对于开发中,这种操作一般比较繁琐。一般使用扫描包的方式来做。通过以包为单位,设置和修改包下的所有类都拥有默认的别名(即是类名),并且所有的别名都不区分大小写。
系统中默认配置的别名:4. environments:设置数据库环境
用于设置多个连接数据库的环境。比如说一些其他数据库,Oracle,MySQL等等,在需要的使用达到动态切换的目的。
而对于要使用哪个环境,是通过default属性来设置对应environment的id来实现的。
一个environments有很多的environment,但是只能有一个作为项目的数据库环境。
<environments default="development">
<!--开发环境
environment: 用来配置某个具体的环境
属性:
id: 表示连接数据库环境的唯一标识,不能重复
属性:
type: = "JDBC | MANAGED"
JDBC: 表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式, 事务的提交和回滚需要手动来进行处理
MANAGED: 被管理,例如spring
-->
<environment id="development">
<!--事务管理器通过jdbc来进行管理-->
<transactionManager type="JDBC"/>
<!--数据源,从数据连接池中进行取用
datasource: 配置数据源
属性:
type: 设置数据源类型
type = "POOLED | UNPOOLED | JNDI"
POOLED: 表示使用数据库连接池缓存数据库连接
UNPOOLED: 表示不使用数据库中的连接词
JNDI: 表示使用上下文中的数据源连接池
-->
<dataSource type="POOLED">
<!--设施连接数据库的驱动-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--设置连接地址-->
<property name="url" value="jdbc:mysql://localhost:3307/mybatis"/>
<!--用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="admin"/>
</dataSource>
</environment>
<!--测试-->
<environment id="test">
</environment>
</environments>
4.1 transactionManager:设置事务管理方式
属性:type=“JDBC/MANAGED”
JDBC: 在当前环境中,执行sql时,使用的是jdbc原生的事务管理方式,需要手动的提交和回滚事务
MANAGED:被管理,例如Spring
5. mappers:引入映射文件
mappers引入映射文件有三种方式(严格来说是四种)。而这里博主只介绍一种常用的方式:🍒以包围单位引入配置文件
但是,以包为单位引入配置文件,有两个要求:
mapper接口所在的包要和映射文件所在的包要一致。
mapper接口要和映射文件的名称一致。
满足了上述两个要求之后,就可以在标签内利用标签实现批量引入映射文件。
<mappers>
<package name="com.fangshaolei.mybatis.mapper"/>
</mappers>
Notice:还有一个需要注意的点,在resources中,如果一次性创建多个层级目录,不能使用如图所示,使用.来创建。而要使用com/fangshaolei/mybatis/mapper才能创建出和类包一样的目录结构。
否则会报:BindingException: Type interface com.atguigu.mybatis.mapper.UserMapper is not known to the MapperRegistry.
6.总结
本节内容,没有什么特殊的tips,对于还有一些其他的配置信息,可以在MyBatis官方文档中有详细说明,博主不在班门弄斧的解释。
一份完整的mybatis-config.xml配置示例:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--引入配置文件-->
<properties resource="jdbc.properties"/>
<!--
mybatis配置文件中一定要按照顺序来进行配置
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,reflectorFactory?,plugins?,environments?,
databaseIdProvider?,mappers?)".
-->
<typeAliases>
<!--进行书写别名
类型别名他不进行区分大小写 ,如果alias不进行设置就会拥有一个默认的别名,默认的就是当前的类名
typealias: 设置某个类型的别名
属性:
type: 设置需要设置别名的类型
alias: 设置某个类型的别名,若不设置该属性,就会拥有一个默认的别名,默认的别名就是当前的类名。
-->
<!--<typeAlias type="com.fangshaolei.mybatis.pojo.User" alias="user"/>-->
<package name="com.fangshaolei.mybatis.pojo"/> <!--通过以包为单位来进行设置-->
</typeAliases>
<!--
environments: 配置多个连接数据的环境
属性:
default: 设置默认使用的环境id
-->
<environments default="development">
<!--开发环境
environment: 用来配置某个具体的环境
属性:
id: 表示连接数据库环境的唯一标识,不能重复
属性:
type: = "JDBC | MANAGED"
JDBC: 表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式, 事务的提交和回滚需要手动来进行处理
MANAGED: 被管理,例如spring
-->
<environment id="development">
<!--事务管理器通过jdbc来进行管理-->
<transactionManager type="JDBC"/>
<!--数据源,从数据连接池中进行取用
datasource: 配置数据源
属性:
type: 设置数据源类型
type = "POOLED | UNPOOLED | JNDI"
POOLED: 表示使用数据库连接池缓存数据库连接
UNPOOLED: 表示不使用数据库中的连接词
JNDI: 表示使用上下文中的数据源连接池
-->
<dataSource type="POOLED">
<!--设施连接数据库的驱动-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--设置连接地址-->
<property name="url" value="jdbc:mysql://localhost:3307/mybatis"/>
<!--用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="admin"/>
</dataSource>
</environment>
<!--测试-->
<environment id="test">
<!--事务管理器通过jdbc来进行管理-->
<transactionManager type="JDBC"/>
<!--数据源,从数据连接池中进行取用 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--映入配置文件-->
<mappers>
<!-- <mapper resource="mappers/UserMapper.xml"/>-->
<!--
以包围单位引入映射文件
要求:
1.mapper接口所在的包要和映射文件所在的包一致
2. mapper接口要和映射文件的名字是一致的
-->
<package name="com.fangshaolei.mybatis.mapper"/>
</mappers>
</configuration>