| name | mybatis-plus-patterns |
| description | MyBatis-Plus 增强 ORM 技能。覆盖 LambdaQueryWrapper vs QueryWrapper选择(始终Lambda)、分页插件(PaginationInnerInterceptor)配置、乐观锁(@Version + OptimisticLockerInnerInterceptor)、逻辑删除(@TableLogic 全局/局部配置)、自动填充(@TableField fill + MetaObjectHandler)、ActiveRecord vs Mapper模式选择、防全表更新(BlockAttackInnerInterceptor)、通用PageQuery抽取。
当用户使用 MyBatis-Plus 进行数据库操作、配置分页乐观锁、选择查询方式时使用。
与 mybatis 技能互补:mybatis 侧重XML/ResultMap/SQL写法,mybatis-plus 侧重增强功能。
|
| license | Apache-2.0 |
MyBatis-Plus 增强 ORM
来源:https://baomidou.com/
GitHub:https://github.com/baomidou/mybatis-plus
Capability Boundaries
✅ Strong Suits
- LambdaQueryWrapper — 类型安全的查询(不用写字符串字段名)
- 分页插件 — PaginationInnerInterceptor 配置与使用
- 乐观锁 — @Version + OptimisticLockerInnerInterceptor
- 逻辑删除 — @TableLogic 全局配置 vs 单表配置
- 自动填充 — @TableField(fill) + MetaObjectHandler 实现
- ActiveRecord vs Mapper — Entity extends Model vs 独立 Mapper 接口选择
- 防全表操作 — BlockAttackInnerInterceptor
- 通用分页封装 — PageQuery → PageDTO 泛型模式
❌ Out of Scope
- MyBatis XML/ResultMap/动态SQL → mybatis-patterns
- MyBatis-Plus Generator 代码生成 → 已有 mybatis-plus-generator 技能
- MyBatis-Plus 数据权限 → 插件体系,非 LLM 常见需求
最精简配置
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
}
mybatis-plus:
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
insert-strategy: not_null
update-strategy: not_null
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
LLM最常犯的错误
| # | 错误 | 正确做法 |
|---|
| 1 | eq("user_name", username) 字符串字段名 | eq(User::getUsername, username) Lambda 类型安全 |
| 2 | 只用 PageHelper 不分页 | 用 IPage + PaginationInnerInterceptor |
| 3 | 不配置乐观锁导致并发覆盖 | @Version + OptimisticLockerInnerInterceptor |
| 4 | UPDATE SET is_deleted = 1 手动删除 | @TableLogic 自动转 UPDATE |
| 5 | 不知道 ActiveRecord 模式 | extends Model → entity.insert/updateById/selectById |
| 6 | 不防全表更新/删除 | BlockAttackInnerInterceptor 阻止无 where 的 UPDATE/DELETE |
| 7 | 分页查询手动计算 total/pages | Page 自动封装 total, pages, records |
| 8 | createTime/updateTime 手动设置 | MetaObjectHandler 自动填充 |
| 9 | Wrapper 用字符串字段写死 | Wrappers.lambdaQuery() + LambdaQueryWrapper |
核心模式
模式 1: LambdaQueryWrapper(始终用 Lambda)
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
wrapper.eq(User::getUsername, "张三")
.ge(User::getAge, 18)
.in(User::getStatus, List.of(1, 2))
.orderByDesc(User::getCreateTime)
.last("limit 10");
List<User> list = userMapper.selectList(wrapper);
List<User> adults = userService.lambdaQuery()
.ge(User::getAge, 18)
.in(User::getStatus, Arrays.asList(1, 2))
.orderByDesc(User::getCreateTime)
.list();
userService.lambdaUpdate()
.eq(User::getUsername, "张三")
.set(User::getEmail, "new@test.com")
.update();
模式 2: 通用分页封装
@Data
public class PageQuery {
@Min(1) private Integer pageNo = 1;
@Min(1) @Max(5000) private Integer pageSize = 10;
private String sortBy;
private boolean isAsc = true;
public <T> Page<T> toPage() {
Page<T> page = Page.of(pageNo, pageSize);
if (StrUtil.isNotBlank(sortBy)) {
page.addOrder(new OrderItem(sortBy, isAsc));
}
return page;
}
}
@Data
public class PageDTO<T> {
private Long total;
private Long pages;
private List<T> list;
public static <T> PageDTO<T> of(IPage<T> page) {
PageDTO<T> dto = new PageDTO<>();
dto.setTotal(page.getTotal());
dto.setPages(page.getPages());
dto.setList(page.getRecords());
return dto;
}
}
public PageDTO<User> queryUsers(PageQuery query) {
Page<User> page = userService.page(query.toPage(),
Wrappers.lambdaQuery<User>().ge(User::getAge, 18));
return PageDTO.of(page);
}
模式 3: 乐观锁 + 逻辑删除 + 自动填充 Entity
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String email;
@Version private Integer version;
@TableLogic private Integer deleted;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
@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());
}
}
模式 4: ActiveRecord vs Mapper 对比
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_user")
public class User extends Model<User> {
@TableId private Long id;
private String username;
}
User user = new User();
user.setUsername("张三");
user.insert();
User found = user.selectById(1L);
user.setEmail("new@test.com");
user.updateById();
user.deleteById();
public interface UserMapper extends BaseMapper<User> {}
public interface UserService extends IService<User> {}
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {}
userService.save(user);
userService.getById(1L);
userService.lambdaQuery().eq(User::getUsername, "张三").list();
模式 5: 防全表更新/删除
userService.update(new User().setEmail("all@test.com"));
userService.lambdaUpdate()
.eq(User::getStatus, "ACTIVE")
.set(User::getEmail, "all@test.com")
.update();
Gotchas
- LambdaQueryWrapper 需要注解处理器(annotation processor)生成元数据 — 否则 Lambda 表达式无法解析字段名
- 乐观锁 @Version 字段类型支持 int/Integer/long/Long/Date/LocalDateTime/Timestamp
- 乐观锁必须先查询后更新 —
selectById → setXxx → updateById,否则 version 为 null 不生效
- @TableLogic 影响全局所有删除操作 — deleteById/deleteBatchIds 都会变成 UPDATE
- 自定义 SQL 不自动加逻辑删除条件 — 手写 SQL 需手动拼接
AND deleted = 0
- BlockAttackInnerInterceptor 阻止全表更新 — 开发阶段建议开启,生产按需
- 分页插件在 PageHelper 存在时可能冲突 — 二选一,推荐 MyBatis-Plus 的分页
- ActiveRecord 模式 Entity 必须 extends Model — 否则没有 insert/updateById 等方法
- 逻辑删除后的查询自动带 deleted=0 — 如果需要查已删除的数据,手动
eq(User::getDeleted, 1)
- 自动填充的 MetaObjectHandler 必须是 Spring Bean(@Component) — 否则不生效
- strictInsertFill/strictUpdateFill 要求字段类型精确匹配 — 新版 MyBatis-Plus 推荐用 strict 方法
- Page 构造参数是 pageNo 和 pageSize — 不是 offset 和 limit
Data Privacy
本技能不收集、存储或传输任何用户数据。