首页 > 数据库 >深入剖析 MyBatis-Plus:操作总结、对比与实践案例(CRUD 操作、分页、条件构造器、自动填充、高阶用法:自定义 SQL、乐观锁、多租户支持、扩展 BaseMapper)

深入剖析 MyBatis-Plus:操作总结、对比与实践案例(CRUD 操作、分页、条件构造器、自动填充、高阶用法:自定义 SQL、乐观锁、多租户支持、扩展 BaseMapper)

时间:2024-09-28 11:49:05浏览次数:11  
标签:自定义 租户 BaseMapper CRUD 查询 Plus SQL MyBatis

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 的补充建议

  1. 在简单查询中推荐使用注解方式,避免 XML 配置文件的冗余。
  2. 对于复杂 SQL,建议使用 XML 配置方式,尤其是当 SQL 需要进行动态拼接时,XML 文件的灵活性更高。
  3. 定期审查自定义 SQL 性能,确保查询效率。

2.2 乐观锁

乐观锁是一种常用的并发控制策略,用于解决多线程或多用户操作时的数据一致性问题。MyBatis-Plus 提供了内置的乐观锁支持,基于 @Version 注解实现。当有多个用户同时更新同一条数据时,只有版本号一致的操作会成功,否则会抛出异常。

实现步骤

  1. 在实体类中添加 @Version 注解

    @Version
    private Integer version;
    

    该字段会在每次更新时自动递增,MyBatis-Plus 会基于此字段进行并发控制。

  2. 配置乐观锁插件
    需要在项目中配置乐观锁插件。

    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
    
  3. 使用乐观锁的更新示例
    当更新数据时,MyBatis-Plus 会自动检查 version 字段是否匹配,如果匹配则更新成功,否则操作失败。

    Warehouse warehouse = warehouseMapper.selectById(1L);
    warehouse.setState(1);
    warehouseMapper.updateById(warehouse);  // 乐观锁会自动处理 version 字段
    

乐观锁的应用场景

  • 高并发系统:当多个用户或线程同时操作同一条数据时,通过版本号确保只有一个操作能成功,避免数据不一致。
  • 需要并发安全的业务:如金融系统中的账户余额修改、库存系统中的库存扣减等。

MyBatis-Plus 乐观锁的补充建议

  1. 在高并发系统中使用乐观锁时,建议结合重试机制,避免频繁操作失败。
  2. 定期监控并发失败的情况,评估乐观锁策略的有效性和性能消耗。

3.3 多租户支持

MyBatis-Plus 提供了完善的多租户支持,适用于 SaaS 系统等场景。在多租户系统中,不同的租户共享同一个数据库表,但通过租户 ID 实现数据隔离。MyBatis-Plus 通过租户拦截器来实现 SQL 层的租户隔离,避免手动添加租户 ID 的复杂性。

多租户实现步骤

  1. 配置多租户插件
    需要在 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;
    }
    
  2. 自定义租户 ID 获取逻辑
    在租户拦截器中可以定义获取租户 ID 的逻辑,通常从当前请求的上下文中获取租户信息。

    private Long getCurrentTenantId() {
        // 假设从用户上下文中获取租户ID
        return UserContext.getTenantId();
    }
    
  3. 租户隔离的 SQL 执行
    在添加多租户支持后,所有查询操作将自动附带租户 ID 条件,无需手动修改查询逻辑。(实际开发中不建议用*,不便于维护和可读性差!)

    -- 原始SQL
    SELECT * FROM warehouse;
    
    -- 多租户插件自动拼接的SQL
    SELECT * FROM warehouse WHERE tenant_id = 1;
    

多租户支持的应用场景

  • SaaS 系统:在一个应用中支持多个客户,使用相同的数据库表但进行数据隔离。
  • 需要隔离敏感数据的系统:如财务系统、健康数据系统等。

MyBatis-Plus 多租户支持的补充建议

  1. 租户隔离的复杂度:虽然 MyBatis-Plus 提供了租户自动隔离的功能,但仍需根据业务需求设计不同租户的数据权限,尤其是在权限控制复杂的系统中,租户隔离的逻辑可能会很复杂。
  2. 租户 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 的补充建议

  1. 尽量使用 BaseMapper 提供的通用方法,减少重复代码,提高代码可维护性。
  2. 在自定义方法时,确保 SQL 查询的可读性和性能,避免过度复杂的 SQL 查询。

3. MyBatis 与 MyBatis-Plus 的对比

特性MyBatisMyBatis-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

相关文章

  • 自定义数据源实现读写分离
    说明:读写分离,指把数据库的操作分为读操作、写操作(更新、新增、删除),在多数据库实例(如主从结构)下,把读操作和写操作访问的数据库分开,以此缓解单数据库的压力。读写分离实现的前提,需要数据库之间能同步数据,数据不一致,读写分离没有意义。数据同步可参考下面文章:MySQL主从结构......
  • Ant-design-vue Table 自定义列斑马纹效果
    在AntDesignVue的`Table`组件中,要实现自定义列的斑马纹效果,可以通过设置`rowClassName`属性来实现。以下是一个示例:```html<template> <a-table:data-source="data":rowClassName="rowClassName">  <a-table-columnv-for="columnincolumns":k......
  • 鸿蒙(HarmonyOS)实战开发篇——基于ArkUI现有能力实现自定义弹窗封装方案
    推荐看点鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总【OpenHarmony】鸿蒙南向开发保姆级知识点汇总~持续更新中……场景描述自定义弹窗是应用开发需要实现的基础功能,包括但不限于HarmonyOS开发者文档中定义的模态、半模态、Toast等形式,封装一个好用且和UI组件解耦的弹窗组......
  • SpringBoot整合JPA实现CRUD详解
    SpringBoot版本是2.0以上(2.6.13)JDK是1.8一、依赖<dependencies><!--jdbc--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artif......
  • Ant-design-vue Table 自定义列斑马纹效果
    业务需求在使用ant-design-vue的Table组件的时候,在某个业务模块的内因其承载的功能比较多,各个条件间界定不明显导致感官上十分的模糊,所以需要增加类似斑马纹的填充区分。table组件自带只支持行的斑马纹而我们需要的是列的斑马纹。table组件本身是不支持的所以只能通过其他方......
  • Ant-design-vue Table 自定义列斑马纹效果
    业务需求在使用ant-design-vue的Table组件的时候,在某个业务模块的内因其承载的功能比较多,各个条件间界定不明显导致感官上十分的模糊,所以需要增加类似斑马纹的填充区分。table组件自带只支持行的斑马纹而我们需要的是列的斑马纹。table组件本身是不支持的所以只能通过其他方......
  • Element UI 自定义Layout前端页面布局
    1.layout下新建front文件夹index.vue中内容<template><divclass="frontLayout"><el-container><el-header><divclass="navBar"><divclass="navBarLeft">......
  • JVM自定义类的加载器
    自定义类的加载器咱们书接上回继续说说自定义类类加载器自定义类加载器有什么用?通过类加载器可以实现非常精妙的插件机制。例如:著名的OSGI组件框架,再如Eclipse的插件机制。类加载器为应用程序提供了一种动态增加新功能的机制,这种机制无须重新打包发布应用程序就能实现。......
  • MySQL 表的CRUD与复合查询
    目录表的增删改查Create指定列插入单行数据+全列插入多行数据+全列插入插入否则更新替换(replace)Retrieve标准语法SELECT列全列查询限制显示条目limit(分页查询)基本语法:指定列查询select查询字段为表达式表达式重命名去重WHERE条件比较运算符逻辑运算符案例:结果排序Orde......
  • uniapp [全端兼容] - 详细实现拍照或相册选取图片后插入水印功能,手机拍照或相册上传图
    前言网上的教程乱七八糟且兼容性太差,本文提供优质示例。在uni-app全平台兼容(H5网页网站、支付宝/微信小程序、安卓App、苹果App、nvue)开发中,详解手机从相册选取上传图像后加入水印功能,手机拍摄照相后也可以加入水印,Uniapp给图片添加水印,获取上传或拍摄的图片信息后,为......