BaseMapper接口通过继承即可获得17个通用CRUD方法,如selectById、insert等,无需XML或SQL;需配合@TableName、@TableId注解,实体类须有无参构造器,且仅支持单表操作。

BaseMapper接口怎么用,哪些方法能直接调用
MyBatis-Plus 的 BaseMapper 是个泛型接口,你只要让自己的 Mapper 接口继承它,就自动获得 17 个通用 CRUD 方法,不用写 XML、不用写 SQL。比如 selectById、insert、updateById、deleteById 这些最常用的操作,开箱即用。
注意:这些方法操作的是整个实体类映射的表,默认按主键(id 字段)做条件,且要求实体类用 @TableName 和 @TableId 注解标明表名和主键策略。没标的话,selectById 可能查空,insert 可能报 Unknown column 'id' in 'field list'。
- 实体类必须有无参构造器,否则
selectList等查询会抛InstantiationException -
BaseMapper不提供连表能力,selectJoin这种不存在 —— 它只面向单表 - 方法返回值类型严格对应:比如
selectOne返回T(单个实体),不是List<T>;查不到时返回null,不是空集合
为什么继承了BaseMapper却报错“Cannot resolve method 'xxx'”
常见原因是 Mapper 接口没被 MyBatis-Plus 扫描到,或者继承写错了。最典型的是把 BaseMapper<User> 写成 BaseMapper<user>(首字母小写),或泛型类型和实体类不一致(比如实体叫 UserInfo,但写了 BaseMapper<User>)。
另一个高发场景:项目用了 MyBatis 原生的 Mapper 接口(非 MP 版本),结果在 Spring Boot 启动时提示 Could not find @Mapper annotation 或 Invalid bound statement —— 这说明 MP 的自动代理没生效。
- 确认启动类加了
@MapperScan("com.example.mapper"),且包路径覆盖你的 Mapper 接口 - 检查依赖是否混用了
mybatis-spring-boot-starter和mybatis-plus-boot-starter,二者冲突,只留后者 - IDE 缓存可能导致红标,试试
File → Invalidate Caches and Restart,别光看编辑器报错
insert() 和 save() 有什么区别,该用哪个
insert() 是 BaseMapper 自带的方法,只做单条插入,不处理主键生成逻辑以外的任何事;而 save() 来自 IService(Service 层封装),它内部调用 insert(),但多了判空和主键存在性检查 —— 如果实体的 id 已有值且数据库中已存在,save() 默认会执行更新(取决于 IdType 配置和 updateById 行为)。
所以如果你明确只想插新记录,用 mapper.insert(user) 更直白;如果业务语义是“保存一个用户”,不管新旧,用 userService.save(user) 更稳妥。但别在 Mapper 层调 save() —— 它不属于 BaseMapper,编译就过不去。
-
insert()不校验字段非空,null 值会原样写入数据库(除非字段设了@TableField(fill = FieldFill.INSERT)) -
save()是IService方法,需要你的 Service 实现类继承ServiceImpl<UserMapper, User> - 批量插入别用
insert()循环调用,性能差,改用insertBatchSomeColumn或saveBatch()
QueryWrapper 构造条件时 null 值被忽略,怎么保留
QueryWrapper 默认跳过值为 null 或空字符串的条件,这是设计使然,不是 bug。比如 wrapper.eq("status", user.getStatus()),当 user.getStatus() == null,这行条件根本不会出现在最终 SQL 里。
想强制拼进去(例如查 status 为 NULL 的记录),得用 isNull() 或 apply();想让所有字段都参与构建(包括 null),得换用 UpdateWrapper 的 set() + eq() 组合,或者手动判断:
- 查 status 为 NULL:
wrapper.isNull("status"),不是eq("status", null) - 动态拼接且允许 null:
wrapper.eq(user.getStatus() != null, "status", user.getStatus()) - 模糊查 name 为空串:
wrapper.eq("name", "")有效,但wrapper.like("name", "")会被忽略
真正容易被忽略的是:LambdaQueryWrapper 在编译期绑定字段名,一旦实体类字段改名,它会直接编译失败 —— 这反而是优点,比字符串拼接安全得多,别因为写起来多几个字符就退回去用普通 QueryWrapper。










