告别真实删除的风险,让数据"删除"变得更加安全可靠!

目录

🤔 什么是逻辑删除?

⚙️ 快速实现逻辑删除

1. 添加逻辑删除字段

2. 全局配置

3. 实体类添加注解

🔄 逻辑删除效果

🔍 查询被删除的数据

方式一:自定义SQL

方式二:使用apply方法

🔄 恢复已删除数据

方式一:自定义SQL

方式二:通用更新方法

🔧 局部配置

💡 实用技巧

1. 扩展删除信息

2. 使用拦截器自动填充删除信息

自动记录删除时间和操作人

在MybatisPlusConfig中注册拦截器

🖥️ 实战案例:回收站功能

后端实现

Controller

Service

Mapper

🎨 前端实现 (Vue.js 示例)

📝 小结

⏭️ 下一步学习


🤔 什么是逻辑删除?

逻辑删除是指在数据库中并不真正删除数据,而是通过修改一个标识字段来表示该数据已被"删除"。

删除方式 数据状态 是否可恢复 安全性
物理删除 数据从数据库中彻底消失
逻辑删除 数据仍保留,仅标记为已删除

💡 核心优势

  • 防止数据被误删除导致无法恢复

  • 便于数据的恢复和审计

  • 保留历史数据,用于数据分析

⚙️ 快速实现逻辑删除

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条件,利用索引提高性能

🔥 最佳实践:在大多数业务场景中,推荐使用逻辑删除代替物理删除,以提高系统的健壮性和数据的安全性。

⏭️ 下一步学习

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐