← 返回文章列表

MyBatis-Plus 实战避坑指南

2025/7/20后端开发#Java#MyBatis-plus

MyBatis-Plus 实战避坑指南

前言

想象这样一个场景:你优雅地编写了 MyBatis-Plus 的 MetaObjectHandler,创建、更新操作的公共字段处理得井井有条。然而在某个风和日丽的下午,当你执行 removeById 相关操作时,却发现数据竟然真的被删除了?这是怎么回事?

逻辑删除的配置

在 MyBatis-Plus 中,逻辑删除是一个非常实用的功能。它可以让我们在删除数据时不真正删除记录,而是通过修改某个字段的值来标记数据为已删除状态。

1. 添加逻辑删除字段

首先,我们需要在数据库表中添加一个逻辑删除字段,通常命名为 deletedis_deleted

ALTER TABLE user ADD COLUMN deleted TINYINT(1) DEFAULT 0 COMMENT '逻辑删除标识(0-未删除,1-已删除)';

2. 实体类配置

在实体类中添加对应的字段,并使用 @TableLogic 注解:

@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;
    
    private String email;
    
    @TableLogic
    private Integer deleted;
    
    // 其他字段...
}

3. 全局配置

application.yml 中配置逻辑删除的全局设置:

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted  # 全局逻辑删除的实体字段名
      logic-delete-value: 1        # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0    # 逻辑未删除值(默认为 0)

常见问题与解决方案

问题1:逻辑删除不生效

现象:调用 removeById() 方法后,数据被物理删除而不是逻辑删除。

原因

  1. 实体类中没有添加 @TableLogic 注解
  2. 全局配置不正确
  3. 数据库表中没有对应的逻辑删除字段

解决方案

  1. 确保实体类字段添加了 @TableLogic 注解
  2. 检查全局配置是否正确
  3. 验证数据库表结构

问题2:查询时包含已删除数据

现象:查询结果中包含了逻辑删除的数据。

原因

  1. 查询时使用了原生 SQL
  2. 自定义的 Mapper 方法没有考虑逻辑删除

解决方案

// 错误的做法
@Select("SELECT * FROM user WHERE username = #{username}")
User selectByUsername(String username);

// 正确的做法
@Select("SELECT * FROM user WHERE username = #{username} AND deleted = 0")
User selectByUsername(String username);

// 或者使用 MyBatis-Plus 提供的方法
User user = userMapper.selectOne(
    new QueryWrapper<User>().eq("username", username)
);

最佳实践

1. 统一的基础实体类

创建一个基础实体类,包含公共字段:

@Data
public abstract class BaseEntity {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    
    @TableLogic
    private Integer deleted;
}

2. 自动填充处理器

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
    
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

3. 自定义删除方法

有时候我们需要真正的物理删除,可以自定义方法:

public interface UserMapper extends BaseMapper<User> {
    
    @Delete("DELETE FROM user WHERE id = #{id}")
    int deleteByIdPhysically(Long id);
    
    @Update("UPDATE user SET deleted = 1, update_time = NOW() WHERE id = #{id}")
    int deleteByIdLogically(Long id);
}

总结

MyBatis-Plus 的逻辑删除功能虽然强大,但在使用过程中需要注意配置的完整性和一致性。通过合理的配置和规范的使用,可以大大提高开发效率,同时保证数据的安全性。

记住以下几个要点:

  1. 实体类字段必须添加 @TableLogic 注解
  2. 全局配置要正确设置
  3. 自定义 SQL 要考虑逻辑删除条件
  4. 建立统一的基础实体类和自动填充机制

希望这篇文章能帮助你在使用 MyBatis-Plus 时避免一些常见的坑!