MyBatis-Plus逻辑删除:数据不再真正删除
逻辑删除是指在数据库中并不真正删除数据,而是通过修改一个标识字段来表示该数据已被"删除"。删除方式数据状态是否可恢复安全性物理删除数据从数据库中彻底消失❌低逻辑删除数据仍保留,仅标记为已删除✅高💡核心优势防止数据被误删除导致无法恢复便于数据的恢复和审计保留历史数据,用于数据分析@Mapper@Mapper。
告别真实删除的风险,让数据"删除"变得更加安全可靠!
目录
🤔 什么是逻辑删除?
逻辑删除是指在数据库中并不真正删除数据,而是通过修改一个标识字段来表示该数据已被"删除"。
| 删除方式 | 数据状态 | 是否可恢复 | 安全性 |
|---|---|---|---|
| 物理删除 | 数据从数据库中彻底消失 | ❌ | 低 |
| 逻辑删除 | 数据仍保留,仅标记为已删除 | ✅ | 高 |
💡 核心优势:
防止数据被误删除导致无法恢复
便于数据的恢复和审计
保留历史数据,用于数据分析
⚙️ 快速实现逻辑删除
1. 添加逻辑删除字段
在数据库表中添加一个逻辑删除字段:
ALTER TABLE user ADD COLUMN deleted TINYINT DEFAULT 0 COMMENT '逻辑删除标识:0-未删除,1-已删除';
2. 全局配置
在application.yml中配置:
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 逻辑删除字段名
logic-delete-value: 1 # 逻辑已删除值
logic-not-delete-value: 0 # 逻辑未删除值
3. 实体类添加注解
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
@TableLogic
private Integer deleted;
}
⚠️ 注意:配置完成后,无需修改任何业务代码,MyBatis-Plus会自动处理所有CRUD操作中的逻辑删除。
🔄 逻辑删除效果
配置完成后,MyBatis-Plus会自动处理CRUD操作:
插入操作
插入时会自动设置逻辑删除字段为未删除状态:
User user = new User();
user.setName("张三");
userMapper.insert(user);
// 生成的SQL
// INSERT INTO user (name, deleted) VALUES ('张三', 0)
删除操作
删除操作会转换为更新操作:
userMapper.deleteById(1L);
// 生成的SQL
// UPDATE user SET deleted=1 WHERE id=1 AND deleted=0
查询操作
查询时会自动过滤已删除数据:
List<User> users = userMapper.selectList(null);
// 生成的SQL
// SELECT * FROM user WHERE deleted=0</details>
更新操作
更新时也会自动过滤已删除数据:
User user = new User();
user.setId(1L);
user.setName("张三改名");
userMapper.updateById(user);
// 生成的SQL
// UPDATE user SET name='张三改名' WHERE id=1 AND deleted=0
🔍 查询被删除的数据
有时我们需要查询被"删除"的数据,比如实现回收站功能:
方式一:自定义SQL
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT * FROM user WHERE deleted=1")
List<User> selectDeleted();
}
方式二:使用apply方法
// 查询所有用户,包括已删除的
QueryWrapper<User> query = new QueryWrapper<>();
query.apply("1=1"); // 使用恒为true的条件,避免自动添加逻辑删除条件
List<User> allUsers = userMapper.selectList(query);
🔄 恢复已删除数据
方式一:自定义SQL
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Update("UPDATE user SET deleted=0 WHERE id=? AND deleted=1")
int restore(Long id);
}
方式二:通用更新方法
// 恢复被删除的用户
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", 1L)
.eq("deleted", 1)
.set("deleted", 0);
userMapper.update(null, updateWrapper);
🔧 局部配置
除了全局配置,也可以在实体类字段上单独配置:
// 自定义逻辑删除值
@TableLogic(value = "0", delval = "1")
private Integer deleted;
💡 实用技巧
1. 扩展删除信息
记录更多删除相关信息
@Data
public class User {
// 其他字段...
@TableLogic
private Integer deleted;
private Date deleteTime; // 删除时间
private Long deleteBy; // 删除人ID
private String deleteReason; // 删除原因
}
2. 使用拦截器自动填充删除信息
自动记录删除时间和操作人
@Component
public class LogicDeleteInterceptor implements InnerInterceptor {
@Override
public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) {
// 判断是否为逻辑删除操作
if (ms.getId().endsWith("deleteById")) {
// 获取当前用户ID
Long currentUserId = SecurityUtils.getCurrentUserId();
// 设置删除信息
Map<String, Object> map = (Map<String, Object>) parameter;
map.put("deleteTime", new Date());
map.put("deleteBy", currentUserId);
}
}
}
在MybatisPlusConfig中注册拦截器
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加自定义的逻辑删除拦截器
interceptor.addInnerInterceptor(new LogicDeleteInterceptor());
// ... 其他拦截器
return interceptor;
}
}
🖥️ 实战案例:回收站功能
下面是一个完整的回收站功能前后端实现示例。
后端实现
Controller
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
// 获取已删除用户列表(回收站)
@GetMapping("/deleted")
public List<User> getDeletedUsers() {
return userService.getDeletedUsers();
}
// 恢复用户
@PostMapping("/restore/{id}")
public boolean restoreUser(@PathVariable Long id) {
return userService.restoreUser(id);
}
// 彻底删除用户
@DeleteMapping("/purge/{id}")
public boolean purgeUser(@PathVariable Long id) {
return userService.purgeUser(id);
}
}
Service
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getDeletedUsers() {
// 使用自定义SQL查询已删除用户
return baseMapper.selectDeleted();
}
@Override
public boolean restoreUser(Long id) {
// 使用自定义SQL恢复用户
return baseMapper.restore(id) > 0;
}
@Override
public boolean purgeUser(Long id) {
// 调用物理删除方法
return baseMapper.physicalDelete(id) > 0;
}
}
Mapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 查询已删除用户
@Select("SELECT * FROM user WHERE deleted=1")
List<User> selectDeleted();
// 恢复用户
@Update("UPDATE user SET deleted=0 WHERE id=#{id} AND deleted=1")
int restore(Long id);
// 彻底删除(物理删除)
@Delete("DELETE FROM user WHERE id=#{id}")
int physicalDelete(Long id);
}
🎨 前端实现 (Vue.js 示例)
⚠️ 请注意: 下面的代码是一个 前端Vue组件的示例,用于演示如何与后端API交互。它 不能 直接在Markdown预览中作为交互式表单运行。您需要将此代码集成到您的Vue.js项目中,并安装相关依赖(如
axios)才能看到实际效果。
<template>
<div class="recycle-bin-container">
<h2><i class="fas fa-trash-alt"></i> 回收站</h2>
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>年龄</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="user in deletedUsers" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
<td>{{ user.email }}</td>
<td>
<button class="btn-restore" @click="restoreUser(user.id)">
<i class="fas fa-undo"></i> 恢复
</button>
<button class="btn-purge" @click="purgeUser(user.id)">
<i class="fas fa-fire"></i> 彻底删除
</button>
</td>
</tr>
<tr v-if="deletedUsers.length === 0">
<td colspan="5" class="no-data">回收站是空的</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
deletedUsers: []
};
},
created() {
this.fetchDeletedUsers();
},
methods: {
// 获取回收站列表
async fetchDeletedUsers() {
try {
const response = await axios.get('/api/users/deleted');
this.deletedUsers = response.data;
} catch (error) {
console.error('获取回收站数据失败', error);
}
},
// 恢复用户
async restoreUser(id) {
try {
await axios.post(`/api/users/restore/${id}`);
// 重新加载列表
this.fetchDeletedUsers();
console.log('用户已恢复');
} catch (error) {
console.error('恢复用户失败', error);
}
},
// 彻底删除用户
async purgeUser(id) {
// 弹出确认框,防止误操作
if (!confirm('确定要彻底删除该用户吗?此操作不可恢复!')) {
return;
}
try {
await axios.delete(`/api/users/purge/${id}`);
// 重新加载列表
this.fetchDeletedUsers();
console.log('用户已彻底删除');
} catch (error) {
console.error('彻底删除用户失败', error);
}
}
}
};
</script>
<style scoped>
.recycle-bin-container {
width: 100%;
max-width: 900px;
margin: 20px auto;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
}
h2 {
text-align: center;
margin-bottom: 20px;
}
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th, .data-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
.data-table th {
background-color: #f7f7f7;
}
.btn-restore, .btn-purge {
padding: 6px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
margin-right: 5px;
color: white;
}
.btn-restore {
background-color: #67c23a;
}
.btn-purge {
background-color: #f56c6c;
}
.no-data {
text-align: center;
color: #909399;
padding: 20px 0;
}
</style>
📝 小结
| 特性 | 说明 |
|---|---|
| 透明化 | 开发者无需关心逻辑删除的具体实现,框架自动处理 |
| 安全性 | 避免数据丢失,为数据恢复和审计提供保障 |
| 可扩展 | 支持自定义SQL,可以方便地实现回收站等复杂功能 |
| 高性能 | 查询和更新时自动添加deleted=0条件,利用索引提高性能 |
🔥 最佳实践:在大多数业务场景中,推荐使用逻辑删除代替物理删除,以提高系统的健壮性和数据的安全性。
⏭️ 下一步学习
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)