MyBatis配置文件深入
# 1. 核心配置文件
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

接下来我们一一对这些属性进行讲解。
# 1.1 属性(properties)
实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件,例如:
<!--加载外部的properties文件-->
<properties resource="jdbc.properties"></properties>
2
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:
<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
3
4
5
6
这个例子中的 username 和 password 将会由 properties 元素中设置的相应值来替换。 driver 和 url 属性将会由 config.properties 文件中对应的值来替换。这样就为配置提供了诸多灵活选择。
如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:
- 首先读取在 properties 元素体内指定的属性;
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性;
- 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性;
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。
从 MyBatis 3.4.2 开始,你可以为占位符指定一个默认值。例如:
<dataSource type="POOLED">
<!-- ... -->
<property name="username" value="${username:ut_user}"/> <!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
</dataSource>
2
3
4
# 1.2 类型别名(typeAliases)
在编写XML的时候,经常需要写类的全限定类名,这样的类名非常的长。因此,MyBatis提供了优化方案。类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias type="com.hukai.demo.pojo.Emp" alias="emp"></typeAlias>
</typeAliases>
2
3
当这样配置时,emp 可以用在任何使用com.hukai.demo.pojo.Emp 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<!--批量起别名:该包下所有的类的本身的类名:别名还不区分大小写-->
<package name="com.hukai.demo.pojo"/>
</typeAliases>
2
3
4
在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。若有注解,则别名为其注解值。见下面的例子:
@Alias("author")
public class Author {
...
}
2
3
4
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格:
| 别名 | 映射的类型 | 别名 | 映射的类型 | 别名 | 映射的类型 |
|---|---|---|---|---|---|
| _byte | byte | byte | Byte | string | String |
| _long | long | long | Long | date | Date |
| _short | short | short | Short | decimal | BigDecimal |
| _int | int | int | Integer | bigdecimal | BigDecimal |
| _integer | int | integer | Integer | object | Object |
| _double | double | double | Double | map | Map |
| _float | float | float | Float | hashmap | HashMap |
| _boolean | boolean | boolean | Boolean | list | List |
| arraylist | ArrayList | collection | Collection | iterator | Iterator |
# 1.3 环境配置(environments)
MyBatis可以配置成适应多种环境,这样有利于将Sql映射到多种数据库中。这在实际开发中非常有用。例如:开发、测试、生产环境的数据库资源配置都是不一样的。例如下面的配置:
<environments default="developer"><!-- 使用开发环境 -->
<!-- 开发环境 -->
<environment id="developer">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value=""/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<!-- 测试环境 -->
<environment id="testA">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value=""/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
在创建SqlSessionFactory的时候,可以将需要使用的环境变量传递。如果忽略了environment参数,那么就会使用配置的默认值。
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
2
3
# 1.3.1 事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 J2EE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
2
3
Tips:如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
# 1.3.2 数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
- UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。
- POOLED(推荐)– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。
- JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用
# 1.4 映射器(mappers)
mappers标签目的是在SqlMapConfig配置文件中关联Mapper配置文件。同时也提供了相对比较灵活方式的配置,你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括file:///形式的URL),或类名和包名等。例如:
<!--使用相对于类路径的资源引用-->
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<!--使用完全限定资源定位符(URL)-->
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<!--使用映射器接口实现类的完全限定类名-->
<mapper class="org.mybatis.builder.AuthorMapper"/>
<!--将包内的映射器接口实现全部注册为映射器-->
<package name="org.mybatis.builder"/>
2
3
4
5
6
7
8
# 2. 映射配置文件
# 2.1 insert, update 和 delete
数据变更语句 insert,update 和 delete 的实现非常接近,下面是 insert,update 和 delete 语句的示例:
<mapper namespace="com.hukai.demo.dao.IEmpDao">
<insert id="insert" parameterType="emp">
insert into emp values (#{empno},#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
</insert>
<update id="update" parameterType="emp">
update emp set ename = #{ename} where empno = #{empno}
</update>
<delete id="delete" parameterType="integer">
delete from emp where empno = #{empno}
</delete>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Dao接口的代码太简单了,略过,我们直接上测试的代码:
public class EmpTest {
private SqlSession sqlSession;
private IEmpDao empDao;
@Before
public void before() throws Exception{
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession();
empDao = sqlSession.getMapper(IEmpDao.class);
}
@After
public void after() throws Exception{
sqlSession.commit();
sqlSession.close();
}
@Test
public void testInsert() {
Emp emp = new Emp();
emp.setEmpno(7888);
emp.setEname("hukai");
emp.setJob("MANAGER");
emp.setMgr(7839);
emp.setHiredate(new Date());
emp.setSal(8888D);
emp.setDeptno(10);
empDao.insert(emp);
}
@Test
public void testUpdate() {
Emp emp = new Emp();
emp.setEmpno(7888);
emp.setEname("HUKAI");
empDao.update(emp);
}
@Test
public void testDelete() {
empDao.delete(7888);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
注意:insert, update 和 delete操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务,即sqlSession.commit()。
# 2.2 动态sql语句
MyBatis强大并且受到众多开发者青睐的原因之一就是强大的动态Sql能力。如果我们开发中使用的Sql语句都是非常简单的,那么没有问题。但是遇到复杂的业务场景,需要随时根据不同的条件执行不同的查询语句,这个时候就不应该写多个sql语句。而是应该编写一个随时根据不同条件调整的sql语句。
下面我们介绍常用的一下动态sql标签。
# 2.2.1 if标签
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:
<select id="findByCondition" resultType="emp" parameterType="emp">
select * from emp where 1=1
<if test="ename != null and ename != ''">
and ename like concat('%',#{ename},'%')
</if>
<if test="job != null and job != ''">
and job = #{job}
</if>
</select>
2
3
4
5
6
7
8
9
有同学注意到我在where字句后面跟了个1=1,因为如果不加这个,当ename和job都为空时,这条语句会变成:
select * from emp where
很明显这条语句执行会失败。虽然上面这种做法可以避免这个问题,但并不是很优雅,所以我们引入where标签。
# 2.2.2 where标签
由于查询条件的不同,关键字where和and都没有实现动态。这个时候我们需要引入新的标签实现where和and的动态生成:
<select id="findByCondition" resultType="emp" parameterType="emp">
select * from emp
<where>
<if test="ename != null and ename != ''">
and ename like concat('%',#{ename},'%')
</if>
<if test="job != null and job != ''">
and job = #{job}
</if>
</where>
</select>
2
3
4
5
6
7
8
9
10
11
使用where标签包裹if查询条件,如果没有任何一个if条件满足,where就不会拼接。而且where会将第一个条件的and或者or关键字去掉
# 2.2.3 choose、when、otherwise标签
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。 choose的条件判断和if标签的写法一样。
<select id="findByCondition" resultType="emp" parameterType="emp">
select * from emp
<where>
<choose>
<when test="salgrade == 1">
and sal > 700 and sal <= 1200
</when>
<when test="salgrade == 2">
and sal > 1200 and sal <= 1400
</when>
<when test="salgrade == 3">
and sal > 1400 and sal <= 2000
</when>
<!-- 如果上面的条件都不满足,就拼接otherwise标签的条件 -->
<otherwise>
and sal > 2000
</otherwise>
</choose>
</where>
</select>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
当传入的salgrade为2时,上述动态sql的结果为:
select * from emp where sal > 1200 and sal <= 1400
# 2.2.4 foreach标签
态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
#{item}
</foreach>
</select>
2
3
4
5
6
7
8
上述动态sql的遍历结果如下:
SELECT * FROM POST P WHERE ID in (id1, id2)
foreach属性解读:
- collection:需要遍历的集合名词
- index:遍历的索引,一般不使用
- item:collection遍历出来的值临时存储在item中
- open:整个遍历过程开始符号
- close:整个遍历过程结束符号
- separator:遍历一次就拼接的符号