MyBatis-Plus简介

官方地址:https://baomidou.com/

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

而本次工作内容就是使用了它的自动填充功能,实现 Entity 对象属性值的自动填充。

自动填充功能

实现功能:在插入或更新数据时,自动插入或更新指定字段的值。例如一些特殊字段:创建时间、创建人、更新时间、更新人等。

实现原理主要分为两步:

  1. 实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
  2. @TableField 注解标记字段的填充策略。

注意事项:

image-20240112152613998

关于注意事项第一点:填充原理是直接给 Entity 的属性设置值,理解起来就是,填充原理是基于 Entity 实例对象的,所以通过 Mapper SQL 语句方式、或者通过 Lambda 表达式方式都是不可行的。

关于注意事项最后一点:解释当需要通过 Mapper SQL 语句方式填充时,必须按照它的约定配置,一要求方法参数对象是 Entity 对象,二要求方法参数类型需要按照约定定义别名。

使用方法

在 Spring Boot 环境下,定义实体对象、Mapper对象、Service对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@TableName("user")
@Data
public class User {

@TableId(type = IdType.ASSIGN_ID)
private Long id;

private String name;

private Integer age;

private String email;

@TableField(fill = FieldFill.INSERT)
private Date createTime;

@TableField(fill = FieldFill.INSERT)
private Long createById;

@TableField(fill = FieldFill.UPDATE)
private Date updateTime;
}
1
2
3
4
@Mapper
public interface UserMapper extends BaseMapper<User> {

}
1
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
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> {

@Transactional
public User save(String name, Integer age, String email) {
// 初始化User对象
User user = new User();
user.setName(name);
user.setAge(age);
user.setEmail(email);

// 保存
this.save(user);

// 查询
return this.getById(user.getId());
}

@Transactional
public User updateByLambda(Long id, Integer age) {
this.lambdaUpdate()
.eq(User::getId, id)
.set(User::getAge, age)
.update();

return this.getById(id);
}

@Transactional
public User updateByMethod(Long id, Integer age) {
User user = new User();
user.setId(id);
user.setAge(age);
this.updateById(user);

return this.getById(id);
}

}

然后,实现 MetaObjectHandler 接口,定义 MyBatis Plus 填充策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
this.strictInsertFill(metaObject, "createById", Long.class, System.currentTimeMillis());
}

@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
}

最后,编写单元测试类,调用 Service 对象的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@SpringBootTest
public class UserServiceTest {

@Autowired
private UserServiceImpl userService;

@Test
public void testSaveUser() {
User user = userService.save("wray", 18, "wray20156294@gmail.com");
System.out.println(user);
}

@Test
public void testUpdateByLambda() {
User user = userService.updateByLambda(1745704644463579138L, 19);
System.out.println(user);
}

@Test
public void testUpdateByMethod() {
User user = userService.updateByMethod(1745704644463579138L, 20);
System.out.println(user);
}
}

测试发现,在调用 userService.updateByLambda 方法时,基于 Lambda 表达式修改的数据,填充策略没有生效,证实了注意事项的第一点。


完整项目示例代码:https://github.com/wangfarui/work-report/tree/main/mybatis-plus-fill

关于 @TableField 注解标记字段填充策略的疑问

自动填充功能的两步实现原理中,第二步要求指定字段标记填充策略。一开始我认为有点搞繁琐了,因为既然已经在第一步配置填充策略时指定了填充字段的名称,为何还要再标记说明一遍。

这就得先搞清楚 MyBatis-Plus 是如何实现此功能的了,假如没有第二步,现在有一个 Entity 对象需要保存,填充策略自动填充值是填充的属性值,其实并没有直接给实体对象赋值。等到后面拼接保存方法的sql语句时,判断实体对象是否存在相同字段名称的属性,然后拼接sql语句,实现字段自动填充。

上述是指实体对象确实需要自动填充,假如某个表也有相同字段,但是它不需要自动填充,如果没有第二步标记指定,就得把业务表是否填充的判断逻辑放到 MyBatis-Plus 的 MetaObjectHandler 填充方法中了,这样一个为了简化代码的自动填充功能反而变得复杂了。

因此,MyBatis-Plus 在实现自动填充功能时,不仅需要判断实体对象是否存在相同字段名称的属性,还要判断该属性是否被标记。

总结

MyBatis-Plus 的自动填充功能,在业务项目下还是很实用的,业务项目的数据表基本上都要求要有创建信息、更新信息等,这些基础数据在业务代码中会频繁出现且代码内容完全相同,使用填充功能还可以避免大量重复代码。