MyBatis-Plus 是 MyBatis 的增强工具,它极大简化了 MyBatis 的配置和操作,提高了开发效率。本文从基本操作到高阶用法,详细介绍了 MyBatis-Plus 的常见功能及与 MyBatis 的区别,并通过实际案例展示其强大的扩展能力。
MyBatis-Plus 基于 MyBatis,但旨在减少开发者的代码量,增强可维护性。它以“无侵入”为设计理念,使得用户能够在 MyBatis 的基础上无缝升级,保留原有特性,同时加入更多自动化功能,如 CRUD 操作、分页、条件构造器等。
1. 常用操作总结
1.1 CRUD 操作
MyBatis-Plus 提供了简单的 BaseMapper
接口,几乎可以实现所有常见的 CRUD 操作,无需手动编写 SQL。
操作 | 方法 | 说明 |
---|---|---|
插入 | insert(T entity) | 插入一条记录,返回影响行数 |
删除(按主键) | deleteById(Serializable id) | 根据主键删除数据 |
删除(条件) | delete(QueryWrapper<T> query) | 根据条件删除 |
更新 | updateById(T entity) | 根据主键更新数据 |
查询(按主键) | selectById(Serializable id) | 根据主键查询 |
查询(条件) | selectList(QueryWrapper<T> query) | 根据条件查询列表 |
3.2 条件构造器(Wrapper)
MyBatis-Plus 提供了丰富的条件构造器,简化查询条件的编写。例如:
QueryWrapper<Warehouse> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("state", 1); // 查询 state=1 的记录
List<Warehouse> list = warehouseMapper.selectList(queryWrapper);
方法 | 作用 | 示例 |
---|---|---|
eq | 等于 | eq("state", 1) |
ne | 不等于 | ne("state", 0) |
gt | 大于 | gt("capacity", 100) |
between | 区间查询 | between("capacity", 100, 200) |
like | 模糊查询 | like("name", "仓库") |
3.3 分页功能
MyBatis-Plus 内置了分页插件,使用简单。只需在调用分页查询时传入分页对象,MyBatis-Plus 会自动生成分页 SQL。
Page<Warehouse> page = new Page<>(1, 10); // 第1页, 每页10条
IPage<Warehouse> result = warehouseMapper.selectPage(page, null);
参数 | 含义 |
---|---|
page.getCurrent() | 当前页数 |
page.getSize() | 每页显示条数 |
page.getTotal() | 总记录数 |
3.4 自动填充
可以通过配置自动填充功能,在插入或更新记录时自动设置某些字段的值(如创建时间、更新时间等)。配置如下:
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
然后在 MyBatis-Plus 中定义 MetaObjectHandler
来处理自动填充逻辑:
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
}
2. 高阶用法
MyBatis-Plus 提供了丰富的高阶功能,用于处理复杂的业务场景。以下部分将详细讲解这些高阶功能,包括其使用方式、应用场景和与 MyBatis 原生实现的区别。
2.1 自定义 SQL
尽管 MyBatis-Plus 大多数 CRUD 操作已自动生成,但在一些复杂场景中,开发者可能需要编写特定的 SQL 查询。MyBatis-Plus 完全支持自定义 SQL,通过注解或 XML 文件的方式,灵活实现复杂查询。
注解方式:
通过注解 @Select
、@Insert
、@Update
等直接编写 SQL。适合简单查询场景,代码简洁直观。
@Select("SELECT * FROM warehouse WHERE state = #{state}")
List<Warehouse> selectByState(@Param("state") Integer state);
- 优点:编写方便、直观,直接写在接口中,减少了 XML 文件配置的麻烦。
- 缺点:对于复杂 SQL 查询,这种方式不够直观,可读性较差。
XML 文件方式:
更复杂的 SQL 可以通过 XML 配置文件来实现。MyBatis-Plus 完全兼容 MyBatis 原生的 XML 配置风格,复杂查询时推荐这种方式,便于管理和调试。
XML 配置示例:
<mapper namespace="com.example.mapper.WarehouseMapper">
<select id="selectByState" parameterType="java.lang.Integer" resultType="com.example.entity.Warehouse">
SELECT * FROM warehouse WHERE state = #{state}
</select>
</mapper>
- 优点:适合复杂 SQL,便于分离 SQL 和代码逻辑,方便管理和优化。
- 缺点:相比注解方式,配置文件较为冗长,增加维护成本。
MyBatis-Plus 自定义 SQL 的补充建议:
- 在简单查询中推荐使用注解方式,避免 XML 配置文件的冗余。
- 对于复杂 SQL,建议使用 XML 配置方式,尤其是当 SQL 需要进行动态拼接时,XML 文件的灵活性更高。
- 定期审查自定义 SQL 性能,确保查询效率。
2.2 乐观锁
乐观锁是一种常用的并发控制策略,用于解决多线程或多用户操作时的数据一致性问题。MyBatis-Plus 提供了内置的乐观锁支持,基于 @Version
注解实现。当有多个用户同时更新同一条数据时,只有版本号一致的操作会成功,否则会抛出异常。
实现步骤:
-
在实体类中添加
@Version
注解:@Version private Integer version;
该字段会在每次更新时自动递增,MyBatis-Plus 会基于此字段进行并发控制。
-
配置乐观锁插件:
需要在项目中配置乐观锁插件。@Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }
-
使用乐观锁的更新示例:
当更新数据时,MyBatis-Plus 会自动检查version
字段是否匹配,如果匹配则更新成功,否则操作失败。Warehouse warehouse = warehouseMapper.selectById(1L); warehouse.setState(1); warehouseMapper.updateById(warehouse); // 乐观锁会自动处理 version 字段
乐观锁的应用场景:
- 高并发系统:当多个用户或线程同时操作同一条数据时,通过版本号确保只有一个操作能成功,避免数据不一致。
- 需要并发安全的业务:如金融系统中的账户余额修改、库存系统中的库存扣减等。
MyBatis-Plus 乐观锁的补充建议:
- 在高并发系统中使用乐观锁时,建议结合重试机制,避免频繁操作失败。
- 定期监控并发失败的情况,评估乐观锁策略的有效性和性能消耗。
3.3 多租户支持
MyBatis-Plus 提供了完善的多租户支持,适用于 SaaS 系统等场景。在多租户系统中,不同的租户共享同一个数据库表,但通过租户 ID 实现数据隔离。MyBatis-Plus 通过租户拦截器来实现 SQL 层的租户隔离,避免手动添加租户 ID 的复杂性。
多租户实现步骤:
-
配置多租户插件:
需要在 MyBatis-Plus 中配置租户插件,用于在执行 SQL 时自动为查询条件加上租户 ID。@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor(); tenantInterceptor.setTenantLineHandler(new TenantLineHandler() { @Override public Expression getTenantId() { // 从上下文中获取租户ID return new LongValue(getCurrentTenantId()); } @Override public boolean ignoreTable(String tableName) { // 忽略不需要添加租户过滤的表 return "sys_user".equals(tableName); } }); interceptor.addInnerInterceptor(tenantInterceptor); return interceptor; }
-
自定义租户 ID 获取逻辑:
在租户拦截器中可以定义获取租户 ID 的逻辑,通常从当前请求的上下文中获取租户信息。private Long getCurrentTenantId() { // 假设从用户上下文中获取租户ID return UserContext.getTenantId(); }
-
租户隔离的 SQL 执行:
在添加多租户支持后,所有查询操作将自动附带租户 ID 条件,无需手动修改查询逻辑。(实际开发中不建议用*,不便于维护和可读性差!)-- 原始SQL SELECT * FROM warehouse; -- 多租户插件自动拼接的SQL SELECT * FROM warehouse WHERE tenant_id = 1;
多租户支持的应用场景:
- SaaS 系统:在一个应用中支持多个客户,使用相同的数据库表但进行数据隔离。
- 需要隔离敏感数据的系统:如财务系统、健康数据系统等。
MyBatis-Plus 多租户支持的补充建议:
- 租户隔离的复杂度:虽然 MyBatis-Plus 提供了租户自动隔离的功能,但仍需根据业务需求设计不同租户的数据权限,尤其是在权限控制复杂的系统中,租户隔离的逻辑可能会很复杂。
- 租户 ID 的传递与获取:在多租户系统中,租户 ID 是一个关键的上下文信息,必须确保在每个请求中正确传递和获取,避免数据泄露或隔离失效。
2.4 扩展 BaseMapper
MyBatis-Plus 提供了 BaseMapper
接口,该接口包含了大多数常用的 CRUD 操作,但在实际开发中,可能需要扩展该接口以实现更复杂的业务逻辑。
自定义扩展 BaseMapper:
可以通过继承 BaseMapper
接口并添加自定义方法的方式来扩展功能。例如,假设我们需要为仓库管理系统增加一个方法,用于查询所有活跃的仓库。
public interface CustomWarehouseMapper extends BaseMapper<Warehouse> {
@Select("SELECT * FROM warehouse WHERE active = 1")
List<Warehouse> selectActiveWarehouses();
}
应用场景:
- 定制业务逻辑:当业务逻辑较为复杂,标准 CRUD 操作无法满足需求时,可以通过自定义方法扩展功能。
- 性能优化:在一些高性能要求的场景中,可能需要手动编写 SQL,以优化查询效率。
MyBatis-Plus 扩展 BaseMapper 的补充建议:
- 尽量使用
BaseMapper
提供的通用方法,减少重复代码,提高代码可维护性。 - 在自定义方法时,确保 SQL 查询的可读性和性能,避免过度复杂的 SQL 查询。
3. MyBatis 与 MyBatis-Plus 的对比
特性 | MyBatis | MyBatis-Plus |
---|---|---|
SQL 语句编写 | 需要手动编写 SQL | 提供 CRUD 简化操作,复杂 SQL 可自定义 |
配置复杂度 | 需要配置 XML 文件和 Mapper 接口 | 几乎零配置,内置默认逻辑 |
插件支持 | 依赖第三方插件(如分页、乐观锁) | 内置多种常用插件,如分页、乐观锁、多租户 |
代码侵入性 | 需要大量的 XML 和 Java 代码配合 | 代码侵入性低,功能增强且无侵入 |
条件构造器 | 需要手动拼接 SQL 条件 | 提供强大的条件构造器,简化条件查询 |
开发效率 | 手动编写代码,开发效率较低 | 提供丰富的自动化功能,开发效率高 |
4. 案例演示
4.1 仓库管理系统:仓库状态修改案例
假设我们需要实现一个仓库管理系统,用户可以通过 API 修改仓库状态。此功能可以直接利用 MyBatis-Plus 提供的 CRUD 操作和条件构造器,代码简洁明了。
- 实体类:Warehouse
@Data
@TableName("warehouse")
public class Warehouse {
@TableId
private Long id;
private String name;
private Integer state; // 0:正常,1:空
}
- Mapper 接口:WarehouseMapper
public interface WarehouseMapper extends BaseMapper<Warehouse> {}
- Service 层:WarehouseService
@Service
public class WarehouseService {
@Autowired
private WarehouseMapper warehouseMapper;
public void setWarehouseState(Long id, Integer state) {
Warehouse warehouse = warehouseMapper.selectById(id);
if (warehouse != null) {
warehouse.setState(state);
warehouseMapper.updateById(warehouse);
}
}
}
5. 建议与补充
- 关注版本更新:MyBatis-Plus 依赖的功能和插件随着版本更新不断增强,开发者应及时跟进,避免落后。
- 权衡自定义与自动化:尽管 MyBatis-Plus 提供了强大的自动化功能,但在业务复杂时,仍然需要手动编写 SQL。应根据实际场景选择最佳实现方式。
- 代码审查与性能调优:MyBatis-Plus 虽然能减少大量代码,但自动生成的 SQL 可能不总是最优的。在高并发或数据量庞大的场景下,手动优化 SQL 依然非常重要。
6. 结语
MyBatis-Plus 是提升开发效率的利器,适合大多数常规应用场景。在需要快速开发且有标准 CRUD 操作的项目中,其优势尤其明显。对于有复杂业务需求的场景,合理运用自定义 SQL 与自动化功能相结合,能够充分发挥其潜力。
7.相关阅读
也是这个风格文章
标签:自定义,租户,BaseMapper,CRUD,查询,Plus,SQL,MyBatis From: https://blog.csdn.net/hyc010110/article/details/142566368