SpringBoot 整合 MyBatis-Plus 实现逻辑删除与自动填充实战
【摘要】 在业务开发中,很多场景不会直接物理删除数据库数据,而是采用逻辑删除标记数据,同时创建时间、更新时间、操作人这类字段重复赋值十分繁琐。本文结合 MyBatis-Plus 实战,配置逻辑删除与字段自动填充,附带完整代码,讲解底层原理和常见踩坑点,适合后端开发快速落地。
前言
日常业务系统里,订单、用户、文章等数据,一般不使用 DELETE 语句直接删除。物理删除之后数据很难恢复,不利于数据追溯、统计排查。行业主流方案是逻辑删除:增加一个标记字段,删除时更新标记,查询时自动过滤已删除数据。
除此之外,每张表几乎都会有 create_time、update_time、create_by、update_by,每次新增、修改都手动赋值,大量重复样板代码。MyBatis-Plus 提供了自动填充功能,配合逻辑删除配置,一行配置即可生效,极大简化开发。
一、Maven 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
二、数据库表设计
示例用户表 t_user
CREATE TABLE `t_user` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
`username` VARCHAR(50) NOT NULL COMMENT '用户名',
`nickname` VARCHAR(50) COMMENT '昵称',
`create_time` DATETIME COMMENT '创建时间',
`update_time` DATETIME COMMENT '更新时间',
`deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除 0未删除 1已删除'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
约定:0 = 正常,1 = 已删除。
三、YAML 配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
mybatis-plus:
global-config:
db-config:
# 逻辑删除值
logic-delete-value: 1
# 逻辑未删除值
logic-not-delete-value: 0
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
四、实体类编写
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("t_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String nickname;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer deleted;
}
FieldFill.INSERT:新增时自动填充;
FieldFill.INSERT_UPDATE:新增和修改都自动填充。
五、MetaObjectHandler 自动填充处理器
这是自动填充的核心,交给 Spring 管理:
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
}
如果需要填充操作人,从上下文获取当前登录用户ID,在这里扩展即可。
六、Mapper、Service 层
Mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
Service
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> {
}
七、测试 Controller
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {
private final UserServiceImpl userService;
@GetMapping("/add")
public String add() {
User user = new User();
user.setUsername("zhangsan");
user.setNickname("张三");
userService.save(user);
return "新增成功";
}
@GetMapping("/del")
public String del(Long id) {
// 逻辑删除,底层执行 UPDATE t_user SET deleted=1,update_time=? WHERE id=? AND deleted=0
userService.removeById(id);
return "逻辑删除成功";
}
@GetMapping("/page")
public Page<User> page() {
// 自动拼接 WHERE deleted=0,查询不到已删除数据
return userService.page(new Page<>(1,10));
}
}
八、重点:如何查询被逻辑删除的数据
业务上有时候需要后台恢复、查看已删除记录,MP 默认自动过滤,想要查全部,需要写自定义 SQL:
@Select("SELECT * FROM t_user WHERE id = #{id}")
User selectRawById(Long id);
注意:不要直接关闭全局逻辑删除配置,会影响全部查询。
九、常见坑汇总
- 自动填充不生效
- 忘记加
@Component把 MetaObjectHandler 交给Spring; - 实体字段名和填充字符串不一致(大小写、驼峰下划线);
- 手动给字段赋值,手动值优先级高于自动填充。
- 逻辑删除是 UPDATE,不是 DELETE
开启逻辑删除之后,removeById不再执行 delete,变成 update。 - 版本低于 3.3.0 配置方式不一样
新版本推荐全局配置 +@TableLogic。 - 联表查询时,MP 不会自动带上 deleted 条件
多表 join 的自定义 SQL,需要自己手动加上AND deleted = 0。
十、总结
逻辑删除 + 自动填充是后台项目的标配能力。MyBatis-Plus 封装后配置简单,减少大量重复代码。开发时要分清:自带方法自动过滤删除标记,手写自定义SQL必须自己处理删除条件;同时注意自动填充的执行时机,避免和手动赋值冲突。逻辑删除的数据需要规划定期归档清理,防止单表数据无限膨胀影响查询性能。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)