一、核心概念与接口定位

  1. 作用
    • 封装通用 CRUD 操作,减少手动编写 SQL 和 Mapper XML。
    • 提供 17 种标准数据库操作方法(插入、删除、更新、查询)。
  2. 继承关系
    • Mapper 接口需继承 BaseMapper<T>T 为实体类)。
  3. 优势
    • 简化 DAO 层代码,提升开发效率。
    • 支持条件构造器(QueryWrapper/LambdaQueryWrapper)动态生成 SQL。

二、详细操作步骤

1. 环境配置

// 启动类添加 Mapper 扫描
@SpringBootApplication
@MapperScan("com.example.mapper") // 扫描 Mapper 接口所在包
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2. 实体类与 Mapper 定义

// 实体类(使用注解映射表字段)
@Data
@TableName("user") // 指定表名
public class User {
    @TableId(type = IdType.AUTO) // 主键自增策略
    private Long id;
    
    @TableField("user_name") // 字段名映射
    private String name;
    
    private Integer age;
}

// Mapper 接口继承 BaseMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {}

3. CRUD 操作示例

插入数据
User user = new User();
user.setName("John");
user.setAge(30);
int rows = userMapper.insert(user); // 返回受影响行数
System.out.println("插入成功,ID: " + user.getId()); // 自动回填主键

生成 SQLINSERT INTO user (name, age) VALUES (?, ?)

删除数据
// 按 ID 删除
userMapper.deleteById(1L);

// 按条件删除(Lambda 避免硬编码字段名)
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "John").lt(User::getAge, 40);
userMapper.delete(wrapper);

生成 SQLDELETE FROM user WHERE name = 'John' AND age < 40

更新数据
User user = new User();
user.setId(2L);
user.setAge(35);

// 按 ID 更新
userMapper.updateById(user);

// 按条件更新
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(User::getAge, 40).eq(User::getName, "John");
userMapper.update(null, updateWrapper); // 实体对象为 null,仅用 Wrapper 设置值

生成 SQLUPDATE user SET age = 40 WHERE name = 'John'

查询数据
// 按 ID 查询
User user = userMapper.selectById(2L);

// 按条件查询列表
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(User::getName, "J").orderByDesc(User::getAge);
List<User> users = userMapper.selectList(queryWrapper);

// 分页查询(需先配置分页插件)
Page<User> page = new Page<>(1, 10); // 第1页,每页10条
Page<User> result = userMapper.selectPage(page, queryWrapper);
List<User> data = result.getRecords(); // 当前页数据
long total = result.getTotal(); // 总记录数

三、常见错误与解决方案

错误现象 原因 解决方案
NoSuchMethodException: saveBatch BaseMapper 不支持批量插入 使用 Service 层的 saveBatch() 方法替代
Field 'id' doesn't have default 未设置主键策略或数据库未自增 实体类主键添加 @TableId(type = IdType.AUTO) 并确保数据库自增
Invalid bound statement Mapper 接口未被扫描 启动类添加 @MapperScan("包路径") 或接口添加 @Mapper
GC 时间过长(Mapper 接口含 default 方法) 动态代理类频繁创建对象 将 default 方法改为静态工具类方法

四、注意事项

  1. 主键策略

    • IdType.AUTO:依赖数据库自增(需数据库支持)
    • IdType.ASSIGN_ID:雪花算法生成 Long 型 ID(默认)
    • IdType.INPUT:手动输入
  2. 字段映射

    • 数据库字段名与实体属性名不一致时,用 @TableField("column_name") 指定。
    • 排除非表字段:@TableField(exist = false)
  3. 条件构造器使用规范

    • 优先用 LambdaQueryWrapper 避免字段名硬编码(编译期检查)。
    • 避免在循环中创建 Wrapper,防止内存溢出。

五、使用技巧与高级特性

  1. 逻辑删除

    • 添加字段 deleted,注解 @TableLogic
      @TableLogic(value = "0", delval = "1")
      private Integer deleted;
      
    • 配置全局逻辑删除值(application.yml):
      mybatis-plus:
        global-config:
          db-config:
            logic-delete-value: 1
            logic-not-delete-value: 0
      
  2. 自动填充字段

    • 实现 MetaObjectHandler 处理公共字段:
      @Component
      public class AutoFillHandler implements MetaObjectHandler {
          @Override
          public void insertFill(MetaObject metaObject) {
              this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
          }
      }
      
    • 实体类字段添加注解:
      @TableField(fill = FieldFill.INSERT)
      private LocalDateTime createTime;
      
  3. 枚举类型处理

    • 使用 @EnumValue 标记枚举属性映射到数据库:
      public enum Gender {
          MALE(1), FEMALE(2);
          @EnumValue
          private final int code;
      }
      

六、最佳实践与性能优化

  1. 批量操作

    • BaseMapper 无批量插入,需改用 Service 层的 saveBatch() 方法:
      // Service 接口继承 IService
      public interface UserService extends IService<User> {}
      
      // 调用批量插入
      List<User> userList = ...;
      userService.saveBatch(userList, 1000); // 每批 1000 条
      
  2. 分页性能优化

    • 避免大偏移量查询:WHERE id > last_id LIMIT n 替代 LIMIT m, n
    • 配置分页插件最大限制:
      PaginationInnerInterceptor interceptor = new PaginationInnerInterceptor();
      interceptor.setMaxLimit(500L); // 单页最多 500 条
      
  3. 安全防护

    • 启用防全表更新插件:
      MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
      interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 阻止无 WHERE 的更新/删除
      
  4. GC 优化

    • 避免在 Mapper 接口中定义 default 方法(动态代理类易引发 GC 问题),改用静态工具类 。

总结

BaseMapper 的核心价值在于 标准化 CRUD 操作,但需注意:

  • 批量操作 需依赖 Service 层 ;
  • 复杂查询 优先用 Lambda 条件构造器保障可维护性;
  • 性能敏感场景 结合分批次处理与索引优化。
    通过规范使用,可减少 70% 的重复 DAO 代码,同时规避常见陷阱。