适合对象:已经看懂
mybatis-config.xml,想继续理解 XML 中的 SQL 如何对应 Java 方法的同学。
官方参考:
1. XML 映射文件负责什么?
MyBatis 的 XML 映射文件,也常叫 Mapper XML,主要负责写 SQL,并把 SQL 和 Java 调用方式对应起来。
常见有两种调用思路:
| 调用思路 | 说明 |
|---|---|
| 字符串 SQL 标识调用 | 通过 session.selectOne("命名空间.SQL编号", 参数) 调用 |
| Mapper 接口代理调用 | 通过 session.getMapper(接口.class) 获取代理对象,再调用接口方法 |
入门阶段可以先看懂这句话:
XML 的
namespace加 SQL 标签的id,共同决定一条 SQL 的唯一标识。
2. XML 映射文件的基本结构
Mapper XML 通常这样开头:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bytepro.mapper.EmployeeMapper">
...
</mapper>
其中:
| 内容 | 含义 |
|---|---|
DOCTYPE mapper | 使用 MyBatis Mapper XML 的 DTD 规则 |
<mapper> | 一个 SQL 映射文件的根标签 |
namespace | 命名空间,用来给这一组 SQL 做唯一定位 |
namespace 很重要。它既可以是一个普通名称,也可以是 Mapper 接口的全类名。使用接口代理方式时,通常写接口全类名。
3. 字符串 SQL 标识调用
先看一种最直观的写法:
<mapper namespace="EmployeeMapper">
<select id="selectEmployee" resultType="com.bytepro.entity.Employee">
select * from employee where id = #{id}
</select>
</mapper>
Java 中可以这样调用:
Employee employee = session.selectOne("EmployeeMapper.selectEmployee", 1);
匹配关系如下:
EmployeeMapper.selectEmployee
│ │
│ └── select 标签的 id
└── mapper 标签的 namespace
图示:
这种方式容易理解,但字符串写错时,编译器不一定能发现。所以实际开发中更推荐 Mapper 接口代理方式。
4. Mapper 接口代理调用
先定义 Mapper 接口:
public interface EmployeeMapper {
Employee selectEmployee(Integer id);
Long insertEmployee(Employee employee);
Long updateEmployee(Employee employee);
Long deleteEmployee(Integer id);
Employee getEmployeeByMap(Map<String, Object> map);
Employee getEmployeeByIdAndName(@Param("id") Integer id, @Param("name") String name);
}
XML 中让 namespace 对应接口全类名:
<mapper namespace="com.bytepro.mapper.EmployeeMapper">
<select id="selectEmployee" resultType="emp">
select * from employee where id = #{id}
</select>
</mapper>
Java 中这样调用:
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee employee = mapper.selectEmployee(1);
三者之间的对应关系是:
记忆方式很简单:
- 接口全类名对应
namespace - 接口方法名对应 SQL 的
id - 方法参数对应 SQL 里的
#{} - 方法返回值对应 XML 里的
resultType或resultMap
5. select:查询数据
查询单个员工可以这样写:
<select id="selectEmployee" resultType="emp">
select * from employee where id = #{id}
</select>
对应接口方法:
Employee selectEmployee(Integer id);
几个关键点:
| XML 属性或语法 | 含义 |
|---|---|
select | 表示这是一条查询语句 |
id="selectEmployee" | 对应接口里的 selectEmployee 方法 |
resultType="emp" | 查询结果封装成 Employee 对象 |
#{id} | 接收 Java 方法传入的参数 |
调用示例:
Employee employee = mapper.selectEmployee(1);
最终执行时,可以理解为查询:
select * from employee where id = 1
实际执行时 MyBatis 会使用预编译参数,不是简单字符串拼接。这也是 #{} 比直接拼接 SQL 更安全的原因。
6. insert:新增数据并回填自增主键
新增员工可以这样写:
<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="id">
insert into employee(name,gender,age,home_address)
values(#{name},#{gender},#{age},#{homeAddress})
</insert>
对应接口方法:
Long insertEmployee(Employee employee);
调用示例:
Employee employee = new Employee();
employee.setName("李四");
employee.setAge(20);
employee.setGender('1');
employee.setHomeAddress("安徽");
Long result = mapper.insertEmployee(employee);
session.commit();
这段 XML 中最重要的是:
useGeneratedKeys="true" keyProperty="id"
它们用于处理 MySQL 自增主键:
| 属性 | 含义 |
|---|---|
useGeneratedKeys="true" | 使用 JDBC 获取数据库生成的主键 |
keyProperty="id" | 把生成的主键值设置回 Employee 对象的 id 属性 |
也就是说,插入前 employee.getId() 可能是 null;插入成功后,MyBatis 可以把数据库生成的新 id 放回对象中。
#{name}、#{gender}、#{age}、#{homeAddress} 都来自传入的 Employee 对象属性。
7. selectKey:序列主键写法示例
有些数据库常用序列生成主键,可以使用 selectKey。例如:
<insert id="insertEmployee" databaseId="oracle">
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
select EMPLOYEE_SEQ.nextval from dual
</selectKey>
insert into employee(id,name,gender,age,home_address)
values(#{id},#{name},#{gender},#{age},#{homeAddress})
</insert>
这段代码表示:
- 先执行
selectKey中的 SQL,拿到序列生成的主键。 - 把主键设置到
Employee对象的id属性。 - 再执行真正的
insert语句。
如果使用 MySQL 自增主键,通常重点看 useGeneratedKeys="true" 这种写法;如果使用序列主键,可以了解 selectKey。
8. update:修改数据
修改员工可以这样写:
<update id="updateEmployee">
update employee set name=#{name},gender=#{gender},age=#{age},home_address=#{homeAddress}
where id=#{id}
</update>
对应接口方法:
Long updateEmployee(Employee employee);
调用示例:
Employee employee = new Employee();
employee.setId(7);
employee.setName("李四new");
employee.setAge(29);
employee.setGender('1');
employee.setHomeAddress("安徽");
Long result = mapper.updateEmployee(employee);
session.commit();
传入的是一个完整对象,所以 XML 中可以直接通过属性名取值:
#{id}
#{name}
#{gender}
#{age}
#{homeAddress}
修改操作会改变数据库数据,所以执行后需要提交事务。
9. delete:删除数据
删除员工可以这样写:
<delete id="deleteEmployee">
delete from employee where id = #{id}
</delete>
对应接口方法:
Long deleteEmployee(Integer id);
调用示例:
Long result = mapper.deleteEmployee(7);
session.commit();
删除和查询单个员工都可以传入简单参数 id。区别是:
select返回查询结果delete返回影响行数,并且需要提交事务
10. Map 参数:用 key 对应 SQL 参数名
如果一个查询条件由多个值组成,可以传入 Map:
Employee getEmployeeByMap(Map<String, Object> map);
XML:
<select id="getEmployeeByMap" resultType="com.bytepro.entity.Employee">
select * from employee where id = #{id} and name = #{name}
</select>
调用示例:
Map<String, Object> map = new HashMap<>();
map.put("id", 6);
map.put("name", "李四new");
Employee employee = mapper.getEmployeeByMap(map);
当参数是 Map 时:
| Map 的 key | XML 中的写法 |
|---|---|
id | #{id} |
name | #{name} |
也就是说,#{id} 会去 Map 里找 key 为 "id" 的值,#{name} 会去 Map 里找 key 为 "name" 的值。
11. 多参数:用 @Param 起名字
多个普通参数建议使用 @Param 明确命名:
Employee getEmployeeByIdAndName(@Param("id") Integer id, @Param("name") String name);
XML:
<select id="getEmployeeByIdAndName" resultType="com.bytepro.entity.Employee">
select * from employee where id = #{id} and name = #{name}
</select>
@Param 和 XML 参数的关系如下:
| Java 参数 | 注解 | XML 参数 |
|---|---|---|
Integer id | @Param("id") | #{id} |
String name | @Param("name") | #{name} |
调用示例:
Employee employee = mapper.getEmployeeByIdAndName(6, "李四new");
如果没有清晰的参数名,XML 里写 #{id}、#{name} 时就不够直观。@Param 的作用就是把 Java 参数名和 SQL 参数名明确绑定起来。
12. resultType:查询结果怎么变成对象
XML 中可以写完整类名:
resultType="com.bytepro.entity.Employee"
也可以写类型别名:
resultType="emp"
如果 Employee 上有注解:
@Alias("emp")
public class Employee {
private Integer id;
private String name;
private Character gender;
private Integer age;
private String homeAddress;
}
那么 emp 就表示 Employee。
再配合下划线转驼峰配置:
<setting name="mapUnderscoreToCamelCase" value="true"/>
数据库列 home_address 就可以映射到 Java 属性 homeAddress。
简单查询用 resultType 就够了。复杂对象关系映射,比如一对多、多表关联结果封装,通常再学习 resultMap。
13. CRUD 映射总表
| Java 方法 | XML 标签 | SQL id | 参数来源 | 是否需要 commit |
|---|---|---|---|---|
selectEmployee(Integer id) | select | selectEmployee | 简单参数 id | 否 |
insertEmployee(Employee employee) | insert | insertEmployee | Employee 对象属性 | 是 |
updateEmployee(Employee employee) | update | updateEmployee | Employee 对象属性 | 是 |
deleteEmployee(Integer id) | delete | deleteEmployee | 简单参数 id | 是 |
getEmployeeByMap(Map<String, Object> map) | select | getEmployeeByMap | Map 的 key | 否 |
getEmployeeByIdAndName(Integer id, String name) | select | getEmployeeByIdAndName | @Param 指定参数名 | 否 |
14. 这一篇先记住什么?
- XML 的
namespace用来定位一组 SQL。 - Mapper 接口方式中,
namespace通常写接口全类名。 - XML 中 SQL 标签的
id要和接口方法名一致。 #{}是参数占位符,用来接收 Java 传进来的值。resultType决定查询结果封装成什么对象。- 对象参数可以直接通过属性名取值,例如
#{homeAddress}。 - 多个普通参数建议用
@Param命名。 - 新增、修改、删除后要记得
session.commit()。
评论