一、核心概念与接口定位
- 作用
- 封装通用 CRUD 操作,减少手动编写 SQL 和 Mapper XML。
- 提供 17 种标准数据库操作方法(插入、删除、更新、查询)。
- 继承关系
- Mapper 接口需继承
BaseMapper<T>
(T
为实体类)。
- Mapper 接口需继承
- 优势
- 简化 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()); // 自动回填主键
生成 SQL:INSERT 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);
生成 SQL:DELETE 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 设置值
生成 SQL:UPDATE 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 方法改为静态工具类方法 |
四、注意事项
主键策略
IdType.AUTO
:依赖数据库自增(需数据库支持)IdType.ASSIGN_ID
:雪花算法生成 Long 型 ID(默认)IdType.INPUT
:手动输入
字段映射
- 数据库字段名与实体属性名不一致时,用
@TableField("column_name")
指定。 - 排除非表字段:
@TableField(exist = false)
- 数据库字段名与实体属性名不一致时,用
条件构造器使用规范
- 优先用
LambdaQueryWrapper
避免字段名硬编码(编译期检查)。 - 避免在循环中创建 Wrapper,防止内存溢出。
- 优先用
五、使用技巧与高级特性
逻辑删除
- 添加字段
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
- 添加字段
自动填充字段
- 实现
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;
- 实现
枚举类型处理
- 使用
@EnumValue
标记枚举属性映射到数据库:public enum Gender { MALE(1), FEMALE(2); @EnumValue private final int code; }
- 使用
六、最佳实践与性能优化
批量操作
- BaseMapper 无批量插入,需改用 Service 层的
saveBatch()
方法:// Service 接口继承 IService public interface UserService extends IService<User> {} // 调用批量插入 List<User> userList = ...; userService.saveBatch(userList, 1000); // 每批 1000 条
- BaseMapper 无批量插入,需改用 Service 层的
分页性能优化
- 避免大偏移量查询:
WHERE id > last_id LIMIT n
替代LIMIT m, n
。 - 配置分页插件最大限制:
PaginationInnerInterceptor interceptor = new PaginationInnerInterceptor(); interceptor.setMaxLimit(500L); // 单页最多 500 条
- 避免大偏移量查询:
安全防护
- 启用防全表更新插件:
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 阻止无 WHERE 的更新/删除
- 启用防全表更新插件:
GC 优化
- 避免在 Mapper 接口中定义
default
方法(动态代理类易引发 GC 问题),改用静态工具类 。
- 避免在 Mapper 接口中定义
总结
BaseMapper 的核心价值在于 标准化 CRUD 操作,但需注意:
- 批量操作 需依赖 Service 层 ;
- 复杂查询 优先用 Lambda 条件构造器保障可维护性;
- 性能敏感场景 结合分批次处理与索引优化。
通过规范使用,可减少 70% 的重复 DAO 代码,同时规避常见陷阱。