【MyBatis-Plus】条件构造器与高级功能:Wrapper、分页、逻辑删除
🔑 关键词:Java、MyBatis-Plus、LambdaQueryWrapper、分页插件、逻辑删除、乐观锁、自动填充
一、条件构造器 Wrapper
1.1 Wrapper 体系
| 类 | 用途 |
|---|---|
QueryWrapper |
条件查询,用字符串表示列名 |
LambdaQueryWrapper |
条件查询,用 Lambda 表示列名(推荐) |
UpdateWrapper |
条件更新,用字符串表示列名 |
LambdaUpdateWrapper |
条件更新,用 Lambda 表示列名(推荐) |
💡 推荐 Lambda 风格:编译期检查,避免列名写错。
1.2 LambdaQueryWrapper 常用方法
java
// 等价 SQL: SELECT * FROM animal WHERE name = '大熊猫' AND age > 3
List<Animal> list = animalMapper.selectList(
new LambdaQueryWrapper<Animal>()
.eq(Animal::getName, "大熊猫")
.gt(Animal::getAge, 3)
);
常用条件方法:
| 方法 | SQL | 示例 |
|---|---|---|
eq |
= |
.eq(Animal::getAge, 5) |
ne |
!= |
.ne(Animal::getStatus, 0) |
gt |
> |
.gt(Animal::getAge, 3) |
ge |
>= |
.ge(Animal::getAge, 3) |
lt |
< |
.lt(Animal::getAge, 10) |
le |
<= |
.le(Animal::getAge, 10) |
like |
LIKE '%值%' |
.like(Animal::getName, "熊") |
likeLeft |
LIKE '%值' |
.likeLeft(Animal::getName, "熊") |
likeRight |
LIKE '值%' |
.likeRight(Animal::getName, "熊") |
between |
BETWEEN a AND b |
.between(Animal::getAge, 1, 10) |
in |
IN (a, b) |
.in(Animal::getId, List.of(1,2,3)) |
orderByDesc |
ORDER BY col DESC |
.orderByDesc(Animal::getAge) |
orderByAsc |
ORDER BY col ASC |
.orderByAsc(Animal::getName) |
last |
拼接任意 SQL | .last("LIMIT 10") |
1.3 组合条件(OR / AND)
java
// 等价: WHERE name LIKE '%熊%' AND (age > 3 OR age < 1)
List<Animal> list = animalMapper.selectList(
new LambdaQueryWrapper<Animal>()
.like(Animal::getName, "熊")
.and(w -> w.gt(Animal::getAge, 3).or().lt(Animal::getAge, 1))
);
1.4 LambdaUpdateWrapper
java
// 等价: UPDATE animal SET age = 6 WHERE name = '大熊猫'
animalMapper.update(null,
new LambdaUpdateWrapper<Animal>()
.set(Animal::getAge, 6)
.eq(Animal::getName, "大熊猫")
);
// setSql 自定义
animalMapper.update(null,
new LambdaUpdateWrapper<Animal>()
.setSql("age = age + 1")
.eq(Animal::getId, 1L)
);
二、分页插件
2.1 配置分页插件
java
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
2.2 使用分页
java
// 查询第1页,每页10条
Page<Animal> page = new Page<>(1, 10);
Page<Animal> result = animalMapper.selectPage(page,
new LambdaQueryWrapper<Animal>()
.gt(Animal::getAge, 3)
.orderByDesc(Animal::getCreateTime)
);
// 获取分页结果
result.getTotal() // 总记录数
result.getPages() // 总页数
result.getRecords() // 当前页数据
result.hasNext() // 是否有下一页
三、逻辑删除
3.1 什么是逻辑删除?
不真正删除数据,而是标记一个字段表示"已删除"。查询时自动过滤已删除的记录。
3.2 配置
yaml
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 逻辑删除字段
logic-delete-value: 1 # 已删除值
logic-not-delete-value: 0 # 未删除值
3.3 实体类
java
@TableName("animal")
public class Animal {
private Long id;
private String name;
@TableLogic // 标记为逻辑删除字段
private Integer deleted;
}
3.4 效果
java
// 执行删除 → UPDATE animal SET deleted=1 WHERE id=1 AND deleted=0
animalMapper.deleteById(1L);
// 查询 → SELECT * FROM animal WHERE deleted=0(自动过滤)
animalMapper.selectList(null);
// 真正删除 → 不加 @TableLogic,使用 mapper.delete() 即可
四、乐观锁
4.1 什么是乐观锁?
通过版本号防止并发更新冲突。每次更新时检查版本号,如果被别人改过则更新失败。
4.2 配置
java
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
4.3 实体类
java
@TableName("animal")
public class Animal {
private Long id;
private String name;
@Version // 版本号字段
private Integer version;
}
4.4 效果
java
// 查询
Animal animal = animalMapper.selectById(1L); // version = 1
// 更新 → UPDATE animal SET name='xxx', version=2 WHERE id=1 AND version=1
animal.setName("金丝猴");
int rows = animalMapper.updateById(animal); // 成功返回1
// 如果此时另一个线程已经更新过,version 变成 2
// 当前线程再更新:WHERE id=1 AND version=1 → 匹配不到,返回0(更新失败)
五、自动填充
5.1 场景
createTime、updateTime 等字段不需要手动设置,自动填充。
5.2 实体类标记
java
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
5.3 实现 MetaObjectHandler
java
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date::new, Date.class);
this.strictInsertFill(metaObject, "updateTime", Date::new, Date.class);
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Date::new, Date.class);
}
}
六、IService 通用 Service
6.1 定义
java
// 接口继承 IService
public interface AnimalService extends IService<Animal> { }
// 实现类继承 ServiceImpl
@Service
public class AnimalServiceImpl extends ServiceImpl<AnimalMapper, Animal>
implements AnimalService { }
6.2 IService 常用方法
| 方法 | 说明 |
|---|---|
save(entity) |
插入一条 |
saveBatch(list) |
批量插入 |
removeById(id) |
根据 ID 删除 |
removeBatchByIds(ids) |
批量删除 |
updateById(entity) |
根据 ID 修改 |
updateBatchById(list) |
批量修改 |
getById(id) |
根据 ID 查询 |
list() |
查询全部 |
page(current, size) |
分页查询 |
count() |
查询总数 |
saveOrUpdate(entity) |
有则更新,无则插入 |
java
// saveBatch 批量插入(IService 提供,默认每批 1000 条)
public void importData(List<Animal> list) {
this.saveBatch(list, 500); // 每批500条
}
七、面试高频问题
Q1:LambdaQueryWrapper 和 QueryWrapper 的区别?
答 :
QueryWrapper用字符串表示列名(如.eq("name", "xxx")),列名写错编译时不会报错;LambdaQueryWrapper用 Lambda 表达式引用实体属性(如.eq(Animal::getName, "xxx")),编译期检查,类型安全,推荐使用。
Q2:MyBatis-Plus 的逻辑删除怎么用?
答 :实体类字段加
@TableLogic注解,配置logic-delete-value和logic-not-delete-value。执行delete时自动转为UPDATE SET deleted=1,查询时自动加WHERE deleted=0。
Q3:MyBatis-Plus 的乐观锁怎么实现?
答 :实体类字段加
@Version注解,配置OptimisticLockerInnerInterceptor。更新时 SQL 自动拼接WHERE version=旧版本号,并将 version+1。如果版本号不匹配则更新失败(返回0),需要业务层重试。
八、总结
| 功能 | 核心类/注解 | 要点 |
|---|---|---|
| 条件构造 | LambdaQueryWrapper | eq/gt/like/between/orderBy |
| 更新构造 | LambdaUpdateWrapper | set/setSql |
| 分页 | PaginationInnerInterceptor | Page 对象 + selectPage |
| 逻辑删除 | @TableLogic | delete 转 update,查询自动过滤 |
| 乐观锁 | @Version + 拦截器 | 版本号比对防并发冲突 |
| 自动填充 | MetaObjectHandler | createTime/updateTime 自动设置 |
| 通用 Service | IService / ServiceImpl | saveBatch/updateBatchById 等 |
💡 学习建议:条件构造器是 MyBatis-Plus 最核心的功能,多写多练,配合动物园管理系统的查询需求实际运用。