MyBatis Mapper(映射器)
映射器是 MyBatis 中最重要的文件,文件中包含一组 SQL 语句(例如查询、添加、删除、修改),这些语句称为映射语句或映射 SQL 语句。
映射器由 Java 接口和 XML 文件(或注解)共同组成,它的作用如下。
- 定义参数类型
- 配置缓存
- 提供 SQL 语句和动态 SQL
- 定义查询结果和 POJO 的映射关系
映射器有以下两种实现方式。
- 通过 XML 文件方式实现,比如我们在 mybatis-config.xml 文件中描述的 XML 文件,用来生成 mapper。
- 通过注解的方式实现,使用 Configuration 对象注册 Mapper 接口。
如果 SQL 语句存在动态 SQL 或者比较复杂,使用注解写在 Java 文件里可读性差,且增加了维护的成本。所以一般建议使用 XML 文件配置的方式,避免重复编写 SQL 语句。
1、XML实现映射器
XML 定义映射器分为两个部分:接口和XML。下面先定义接口 RoleMapper。
package cn.edu.guet.mapper;
import cn.edu.guet.bean.Role;
public interface RoleMapper {
Role getRoleById(String id);
}
RoleMapper.xml 代码如下。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.edu.guet.mapper.RoleMapper">
<select id="getRoleById" parameterType="string" resultType="cn.edu.guet.bean.Role">
SELECT id as roleId,name as roleName FROM role WHERE id=#{value}
</select>
</mapper>
下面对上述 XML 文件进行讲解。
- namespace 用来定义命名空间,该命名空间和定义接口的全限定名一致。
<select>
元素表明这是一条查询语句,属性 id 用来标识这条 SQL。resultType 表示返回的是一个 Role 类型的值。
在 MyBatis 配置文件中添加以下代码。
<mapper resource="mapper/RoleMapper.xml"></mapper>
该语句用来引入 XML 文件,MyBatis 会读取 RoleMapper.xml 文件,生成映射器。
下面进行测试,用 SqlSession 来获取 Mapper,Test 类代码如下。
try {
InputStream in = Resources
.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlsession = sqlSessionFactory.openSession();
RoleMapper roleMapper = sqlsession.getMapper(RoleMapper.class);
Role role = roleMapper.getRoleById("67426c770519471384fa87aa0fabbc45");
System.out.println(role.getRoleName());
} catch (IOException e) {
e.printStackTrace();
}
运行结果如下:
10:57:16 [main] DEBUG cn.edu.guet.mapper.RoleMapper.getRoleById - ==> Preparing: SELECT id as roleId,name as roleName FROM role WHERE id=?
10:57:16 [main] DEBUG cn.edu.guet.mapper.RoleMapper.getRoleById - ==> Parameters: 67426c770519471384fa87aa0fabbc45(String)
10:57:16 [main] TRACE cn.edu.guet.mapper.RoleMapper.getRoleById - <== Columns: roleId, roleName
10:57:16 [main] TRACE cn.edu.guet.mapper.RoleMapper.getRoleById - <== Row: 67426c770519471384fa87aa0fabbc45, 管理员
10:57:16 [main] DEBUG cn.edu.guet.mapper.RoleMapper.getRoleById - <== Total: 1
管理员
2、注解实现映射器
使用注解的方式实现映射器,只需要在接口中使用 Java 注解,注入 SQL 即可。如下所示。
package cn.edu.guet.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Select;
import cn.edu.guet.bean.Role;
public interface RoleMapper {
@Select(value = "SELECT id as roleId,name as roleName FROM role WHERE id=#{value}")
Role getRoleById(String id);
}
这里我们使用了 @Select 注解,并且注入了和 XML 中相同的 select 语句。
如果使用注解和 XML 文件两种方式同时定义,那么 XML 方式将覆盖掉注解方式。
虽然这里注解的方式看起来比 XML 简单,但是现实中我们遇到的 SQL 会比该例子复杂得多。如果 SQL 语句中有多个表的关联、多个查询条件、级联、条件分支等,显然这条 SQL 就会复杂的多,所以并不建议读者使用这种方式。比如下面这条 SQL。
select * from t_user u
left join t_user_role ur on u.id = ur.user_id
left join t_role r on ur.role_id = r.id
left join t_user_info ui on u.id = ui.user_id
left join t_female_health fh on u.id = fh.user_id
left join t_male_health mh on u.id = mh.user_id
where u.user_name like concat('%', ${userName},'%')
and r.role_name like concat('%', ${roleName},'%')
and u.sex = 1
and ui.head_image is not null;
如果把以上 SQL 放到 @Select 注解中,无疑会大大降低代码的可读性。如果同时还要考虑使用动态 SQL 或需要加入其他的逻辑,这样就使得这个注解更加复杂了,不利于日后的维护和修改。
此外,XML 可以相互引入,而注解是不可以的,所以在一些比较复杂的场景下,使用 XML 方式会更加灵活和方便。因此大部分的企业都以 XML 为主,以 XML 方式来创建映射器。当然在一些简单的表和应用中使用注解方式也会比较简单。
3、MyBatis 映射器的主要元素
下面介绍在映射器中可以定义哪些元素,以及它们的作用。
元素名称 | 描述 | 备注 |
---|---|---|
mapper | 映射文件的根节点,只有 namescape 一个属性 | namescape 作用如下:用于区分不同的 mapper,全局唯一绑定DAO接口,即面向接口编程。当 namescape 绑定某一接口后,可以不用写该接口的实现类,MyBatis 会通过接口的完整限定名查找到对应的 mapper 配置来执行 SQL 语句。因此 namescape 的命名必须要跟接口同名。 |
select | 查询语句,最常用、最复杂的元素之一 | 可以自定义参数,返回结果集等 |
insert | 插入语句 | 执行后返回一个整数,代表插入的条数 |
update | 更新语句 | 执行后返回一个整数,代表更新的条数 |
delete | 删除语句 | 执行后返回一个整数,代表删除的条数 |
parameterMap | 定义参数映射关系 | 即将被删除的元素,不建议使用 |
sql | 允许定义一部分的 SQL,然后在各个地方引用它 | 例如,一张表列名,我们可以一次定义,在多个 SQL 语句中使用 |
resultMap | 用来描述数据库结果集与对象的对应关系,它是最复杂、最强大的元素 | 提供映射规则 |
cache | 配置给定命名空间的缓存 | - |
cache-ref | 其它命名空间缓存配置的引用 | - |
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>