MyBatis进行注解开发
如何理解注解开发
在 Spring Boot 中使用 MyBatis 注解开发替代 XML 配置,核心是通过
MyBatis 提供的注解直接在 Mapper 接口上编写 SQL,无需编写 Mapper 中 XML
文件的那些</select>等这种标签
使用注解实现基本 CURD
MyBatis 注解开发的核心是在 Mapper 接口 上通过注解编写 SQL,常用注解如下
| 注解 | 作用 | 示例 |
|---|---|---|
@Mapper |
标记接口为 MyBatis Mapper | 加在接口上(或用 @MapperScan 替代) |
@Select |
编写查询 SQL | @Select("SELECT * FROM user WHERE id = #{id}") |
@Insert |
编写插入 SQL | @Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})") |
@Update |
编写更新 SQL | @Update("UPDATE user SET age = #{age} WHERE id = #{id}") |
@Delete |
编写删除 SQL | @Delete("DELETE FROM user WHERE id = #{id}") |
@Results |
自定义结果映射(字段映射) | 解决字段名与属性名不一致 |
@Result |
单个字段的映射规则 | 配合 @Results 使用 |
@Param |
给参数命名(多参数时必用) | 方法参数前加 @Param("name") String name |
@ResultType |
指定返回值类型 | 当返回值类型无法自动推断时,显式指定 |
@Options |
配置 SQL 执行选项 | 如自增主键、批量操作、超时时间等 |
那么,完整的 CURD 实现,如下,就改写我们之前的例子吧
实体类如下
1
2
3
4
5
6
7
public class User {
private Long id; // 主键
private String userName; // 用户名(对应数据库 user_name,开启驼峰自动映射)
private Integer age; // 年龄
private String email; // 邮箱
}UserMapper可以这样被改写,目标是移除UserMapper.xml,改用 MyBatis 注解编写 SQL1
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
46
47
48
49
50public interface UserMapper {
// 增 - 替代insertUser的XML配置
// 自动回填主键
int insertUser(User user);
// 删 - 替代deleteUserById的XML配置
int deleteUserById(Integer id);
// 改 - 替代updateUser的XML配置(动态SQL)
int updateUser(User user);
// 查单个 - 替代findUserById的XML配置
User findUserById(Integer id);
// 查全部 - 替代findAllUser的XML配置
List<User> findAllUser();
// 多条件查询 - 替代findUserByNameAndAge的XML配置(动态SQL)
List<User> findUserByNameAndAge( String name,
Integer minAge);
}然后调整 application.yaml 配置
就是移除 MyBatis 对 XML 文件的扫描
1
2
3
4
5
6
7
8
9
10
11# MyBatis配置
mybatis:
# 移除XML扫描(注解开发不需要)
# mapper-locations: classpath:mapper/*.xml
# 实体类别名包
type-aliases-package: hbnu.project.mybatisdemo.entity
configuration:
# 驼峰命名自动映射
map-underscore-to-camel-case: true
# 打印SQL日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
这样,就能够使用注解进行开发了,可以看到就是注解然后内部写 SQL 语句
其中,插入的自动回填主键需要这样配置,基本就用到这两个配置项
1 |
useGeneratedKeys = true:告诉 MyBatis 使用数据库的主键自增功能。keyProperty = "id":指定将生成的主键值回填到实体类的哪个属性中。
那么,动态 SQL 可以看到也有一些区别
- 对于动态 SQL 的支持,注解中可直接嵌入 XML 风格的动态 SQL
标签(
if/where/foreach/set等),解决复杂条件查询和更新- 注意:动态 SQL 标签需用
<script> ... </script>包裹,才能被 MyBatis 识别 - 注解开发虽便捷,但并非所有场景都适用,需根据业务选择,一般情况下,复杂 SQL 还是在 Mapper XML 中配置的多一些
- 注意:动态 SQL 标签需用
也可以使用注解添加缓存,一级缓存默认开启,二级缓存需要在 Mapper
接口上添加 @CacheNamespace 注解来开启。
1 |
|
使用注解实现复杂关系映射开发
了解使用注解进行复杂关系开发
实现复杂关系映射之前我们可以在映射文件中通过配置<resultMap>来实现,在使用注解开发时我们需要借助@Result注解,@Result
注解等加上@One 注解,@Many
注解等注解实现询,替代 XML 中的 <association> 和
<collection> 标签。
复杂关系映射的注解如下
| 注解 | 核心作用 | 对应 XML 标签 | 适用场景 |
|---|---|---|---|
@Results |
定义一组结果映射规则(可复用),是复杂映射的 “容器” | <resultMap> |
所有复杂映射的基础 |
@Result |
单个字段 / 关联关系的映射规则,可配置普通字段或关联查询 | <result>/<association>/<collection> |
普通字段映射 + 关联关系映射 |
@One |
嵌套查询(一对一),指定关联的单条结果查询方法 | <association> |
一对一关联(如用户 - 身份证) |
@Many |
嵌套查询(一对多),指定关联的多条结果查询方法 | <collection> |
一对多关联(如用户 - 订单) |
@ResultMap |
复用已定义的 @Results 规则,避免重复代码 |
resultMap="xxx" |
多个查询复用同一套映射规则 |
@Param |
关联查询中传递参数,解决多参数绑定问题 | - | 关联查询的参数传递 |
MyBatis 复杂映射的两种核心方式
- 嵌套查询
- 先查询主表数据,再根据主表的关联字段,如外键,调用另一个 Mapper 方法查询关联表数据;
- 注解开发更友好
- 嵌套结果
- 通过一次多表联查(JOIN)获取所有数据,再通过注解拆分结果到关联对象,一次 SQL 查询就行
- 复杂联查 SQL 写在注解中可读性差,一般是 XML 进行编写
一对一实际场景
用户(User)和身份证(IdCard)是一对一关系,一个用户只有一个身份证,身份证外键
user_id 关联用户主键 id。
我们改写我们的实体类
1 |
|
那么,身份证实体类如下
1 |
|
那么,开始编写 Mapper
1 | public interface IdCardMapper { |
修改 application.yaml,新增 MyBatis 注解 Mapper 扫描配置,替代 XML
的<mappers>标签
1 | mybatis: |
一对多实际场景
首先编写对应的实体类
1 |
|
1 |
|
然后编写两个对应的 Mapper,很明显,我们需要根据 id 查订单里对应的订单项
1 | public interface OrderInfoMapper { |
- 那么,就使用
@Results进行映射,把实体类中的字段正确通过@Result映射到数据库中的字段(数据库查询语句中的字段,因为可能涉及到别名) - 然后再使用
@Result,把要查询的一对多中的多,丢给OrderItemMapper中的查询,去做一查多
1 | public interface OrderItemMapper { |
那么,Mapper 就写完了,服务层和控制器层就略了
测试一下,可以看到根据订单的 id 正确返回了多个订单项
多对多实际场景
这次使用课程和学生的对应关系,一门课程有多个学生选修,把上面没用过的@ResultMap给用一下
多对多关系需中间表关联课程和学生
1 |
|
1 |
|
Mapper
层核心是多对多,也是使用@Results定义结果映射的规则,然后使用
@ResultMap
在需要的地方复用映射规则,就例如我查出一个学生对应的课程用@Results定义好了,在下面查询多个学生的时候,直接使用
@ResultMap复用这个一对多的映射规则去做多对多
1 | public interface StudentMapper { |
1 | public interface CourseMapper { |
然后我们来测试一下




