MyBatis-Flex:让MyBatis开发效率飞起来的黑科技!

引言

如果你是一名Java开发者,一定对MyBatis不陌生。作为一款优秀的ORM框架,MyBatis以其灵活性和高性能深受开发者喜爱。然而,随着业务复杂度的提升,原生MyBatis在动态SQL、多表关联、代码生成等方面的短板逐渐暴露。

有没有一款工具,能在保留MyBatis优势的同时,弥补它的短板进而大幅提升开发效率?
答案是:MyBatis-Flex!

今天,我们就来深入解析这款MyBatis增强工具,看看它是如何让数据库操作变得更简单、更优雅的!


一、 MyBatisFlex是什么?

MyBatis-Flex 是一个优雅的 MyBatis 增强框架,能够极大地提高我们的开发效率和开发体验,让我们有更多的时间专注于自己的事情。它的特征是:

✅ 轻量除了 MyBatis,没有任何第三方依赖、没有任何拦截器,其原理是通过 SqlProvider 的方式实现的。同时,在执行的过程中,没有任何的 Sql 解析(Parse)。这带来了几个好处:1、极高的性能;2、极易对代码进行跟踪和调试; 3、更高的把控性。
✅ 灵活支持 Entity 的增删改查、以及分页查询的同时,MyBatis-Flex 提供了 Db + Row工具,可以无需实体类对数据库进行增删改查以及分页查询。 与此同时,MyBatis-Flex 内置的 QueryWrapper 可以轻易的帮助我们实现 多表查询、链接查询、子查询 等等常见的 SQL 场景。
 强大支持任意关系型数据库,还可以通过方言持续扩展,同时支持 多(复合)主键、逻辑删除、乐观锁配置、数据脱敏、数据审计、 数据填充 等等功能。

二、同类框架对比

MyBatis-Flex 主要是和 MyBatis-Plus 与 Fluent-MyBatis 对比,内容来源其官网、git 或者 网络文章,若有错误欢迎纠正。

  • MyBatis-Plus:老牌的 MyBatis 增强框架,开源于 2016 年。

  • Fluent-MyBatis:阿里云开发的 MyBatis 增强框架(来自于阿里云·云效产品团队)

功能对比

功能或特点

MyBatis-Flex

MyBatis-Plus

Fluent-MyBatis

对 entity 的基本增删改查

分页查询

分页查询之总量缓存

分页查询无 SQL 解析设计(更轻量,及更高性能)

多表查询: from 多张表

多表查询: left join、inner join 等等

多表查询: union,union all

单主键配置

多种 id 生成策略

支持多主键、复合主键

字段的 typeHandler 配置

除了 MyBatis,无其他第三方依赖(更轻量)

QueryWrapper 是否支持在微服务项目下进行 RPC 传输

未知

逻辑删除

乐观锁

SQL 审计

数据填充

数据脱敏

✔️ (收费)

字段权限

✔️ (收费)

字段加密

✔️ (收费)

字典回写

✔️ (收费)

Db + Row

Entity 监听

多数据源支持

借助其他框架或收费

多数据源是否支持 Spring 的事务管理,比如 @Transactional 和 TransactionTemplate 等

多数据源是否支持 "非Spring" 项目

多租户

动态表名

动态 Schema

以上内容来自第三方相关产品的官方文档或第三方平台,若有错误,欢迎纠正。

性能对比

本文主要是展示了 MyBatis-Flex 和 Mybaits-Plus 的「性能」对比。Mybaits-Plus 是一个非常优秀 Mybaits 增强框架, 其开源于 2016 年,有很多的成功案例。

原文地址:https://mybatis-flex.com/zh/intro/benchmark.html

  • MyBatis-Flex 的查询单条数据的速度,大概是 MyBatis-Plus 的 5 ~ 10+ 倍。

  • MyBatis-Flex 的查询 10 条数据的速度,大概是 MyBatis-Plus 的 5~10 倍左右。

  • Mybatis-Flex 的分页查询速度,大概是 Mybatis-Plus 的 5~10 倍左右。

  • Mybatis-Flex 的数据更新速度,大概是 Mybatis-Plus 的 5~10+ 倍。

三、支持的数据库类型

数据库

描述

mysql

MySQL 数据库

mariadb

MariaDB 数据库

oracle

Oracle11g 及以下数据库

oracle12c

Oracle12c 及以上数据库

db2

DB2 数据库

H2

H2 数据库

hsql

HSQL 数据库

sqlite

SQLite 数据库

postgresql

PostgreSQL 数据库

sqlserver2005

SQLServer2005 数据库

sqlserver

SQLServer 数据库

dm

达梦数据库

xugu

虚谷数据库

kingbasees

人大金仓数据库

phoenix

Phoenix HBase 数据库

gauss

Gauss 数据库

clickhouse

ClickHouse 数据库

gbase

南大通用(华库)数据库

gbase-8s

南大通用数据库 GBase 8s

oscar

神通数据库

sybase

Sybase ASE 数据库

OceanBase

OceanBase 数据库

Firebird

Firebird 数据库

derby

Derby 数据库

highgo

瀚高数据库

cubrid

CUBRID 数据库

goldilocks

GOLDILOCKS 数据库

csiidb

CSIIDB 数据库

hana

SAP_HANA 数据库

impala

Impala 数据库

vertica

Vertica 数据库

xcloud

行云数据库

redshift

亚马逊 redshift 数据库

openGauss

华为 openGauss 数据库

TDengine

TDengine 数据库

informix

Informix 数据库

greenplum

Greenplum 数据库

uxdb

优炫数据库

Doris

Doris数据库

Hive SQL

Hive 数据库

lealone

Lealone 数据库

sinodb

星瑞格数据库

四、快速开始

1.创建数据库表

CREATE TABLE `sys_user` (
                            `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
                            `dept_id` bigint DEFAULT NULL COMMENT '部门id',
                            `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '账号',
                            `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密码',
                            `nickname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '昵称',
                            `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志',
                            `version` int DEFAULT NULL COMMENT '乐观锁',
                            `create_by` bigint DEFAULT NULL COMMENT '创建人',
                            `create_time` datetime DEFAULT NULL COMMENT '创建时间',
                            `update_by` bigint DEFAULT NULL COMMENT '更新人',
                            `update_time` datetime DEFAULT NULL COMMENT '更新时间',
                            PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户信息';

​​​​2.创建 Spring Boot 项目,并添加 Maven 依赖

注意: 如果您当前使用的是 SpringBoot v3.x 版本,需要把依赖 mybatis-flex-spring-boot-starter 修改为:mybatis-flex-spring-boot3-starter 即可。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

    <dependency>
        <groupId>com.mybatis-flex</groupId>
        <artifactId>mybatis-flex-spring-boot-starter</artifactId>
        <version>1.11.0</version>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
</dependencies>

3.对 Spring Boot 项目进行配置

在 application.yml 中配置数据源:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/db_mybatisflex?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: root 

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:

@SpringBootApplication
@MapperScan("com.mybatisflex.demo.mapper")
public class MybatisFlexDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisFlexDemoApplication.class, args);
    }

}

4.编写实体类 和 Mapper 接口

  • 使用 @Table("sys_user") 设置实体类与表名的映射关系

  • 使用 @Id(keyType = KeyType.Auto) 标识主键为自增

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("sys_user")
public class SysUser implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id(keyType = KeyType.Auto)
    private Long id;
    
    private Long deptId;
    
    private String username;
    
    private String password;
    
    private String nickname;
    
    private Boolean deleted;
    
    private Long createBy;
    
    private LocalDateTime createTime;
    
    private Long updateBy;
    
    private LocalDateTime updateTime;
    
    private Integer version;
}

Mapper 接口继承 BaseMapper 接口:

public interface SysUserMapper extends BaseMapper<SysUser> {

}

5.业务层接口和实现

业务接口继承 IService 接口:

public interface ISysUserService extends IService<SysUser> {

}

业务接口实现继承 ServiceImpl 类:

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

}

6.控制层示例

控制层方法调用实现常用增删改查:

@RestController
@RequestMapping("/sys-user")
public class SysUserController {
    
    @Autowired
    private ISysUserService sysUserService;

    @GetMapping("/save")
    public Boolean save() {
        SysUser sysUser = new SysUser();
        sysUser.setDeptId(1L);
        sysUser.setUsername("zhangsan");
        sysUser.setPassword("123456");
        sysUser.setNickname("张三");
        sysUser.setVersion(1);
        sysUser.setDeleted(false);
        sysUser.setCreateBy(1L);
        sysUser.setCreateTime(LocalDateTime.now());
        return sysUserService.save(sysUser);
    }

    @GetMapping("/saveBatch")
    public Boolean saveBatch() {
        List<SysUser> sysUserList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SysUser sysUser = new SysUser();
            sysUser.setDeptId(1L);
            sysUser.setUsername("zhangsan" + i);
            sysUser.setPassword("123456");
            sysUser.setNickname("张三" + i);
            sysUser.setVersion(1);
            sysUser.setDeleted(false);
            sysUser.setCreateBy(1L);
            sysUser.setCreateTime(LocalDateTime.now());
            sysUserList.add(sysUser);
        }
        return sysUserService.saveBatch(sysUserList);
    }
    
    @GetMapping("/removeById")
    public Boolean removeById() {
        return sysUserService.removeById(1L);
    }
    
    @GetMapping("/removeByIds")
    public Boolean removeBatchByIds() {
        return sysUserService.removeByIds(Collections.singletonList(1L));
    }
    
    @GetMapping("/remove")
    public Boolean remove() {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(SysUser::getDeptId, 1L);
        return sysUserService.remove(queryWrapper);
    }
    
    @GetMapping("/update")
    public Boolean update() {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(SysUser::getDeptId, 1L);
        SysUser sysUser = new SysUser();
        sysUser.setDeptId(2L);
        return sysUserService.update(sysUser, queryWrapper);
    }
    
    @GetMapping("/updateById")
    public Boolean updateById() {
        SysUser sysUser = new SysUser();
        sysUser.setId(1L);
        sysUser.setNickname("李rr");
        sysUser.setVersion(2);
        return sysUserService.updateById(sysUser);
    }
    
    @GetMapping("/updateBatchById")
    public Boolean updateBatchById() {
        List<SysUser> sysUserList = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            SysUser sysUser = new SysUser();
            sysUser.setId((long) i);
            sysUser.setNickname("李四" + i);
            sysUserList.add(sysUser);
        }
        return sysUserService.updateBatch(sysUserList);
    }
    
    @GetMapping("/page")
    public Page<SysUser> page() {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(SysUser::getDeptId, 1L)
                .orderBy(SysUser::getCreateTime).asc();
        return sysUserService.page(new Page<>(1, 10), queryWrapper);
    }
    
    @GetMapping("/list")
    public List<SysUser> list() {
        QueryWrapper queryWrapper = QueryWrapper.create().orderBy(SysUser::getCreateTime).asc();
        return sysUserService.list(queryWrapper);
    }
    
    @GetMapping("/getById")
    public SysUser getById() {
        return sysUserService.getById(1L);
    }

    @GetMapping("/getOne")
    public SysUser getOne() {
        return sysUserService.getOne(QueryWrapper.create().eq(SysUser::getNickname, "张三"));
    }
}

五、高级用法

前言:联表查询、字段填充、逻辑删除、乐观锁、代码生成

1.创建数据库表


CREATE TABLE `sys_dept` (
                            `id` bigint NOT NULL AUTO_INCREMENT COMMENT '部门id',
                            `parent_id` bigint DEFAULT '0' COMMENT '父部门id',
                            `dept_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '部门名称',
                            `order_num` int DEFAULT '0' COMMENT '显示顺序',
                            `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志',
                            `version` int DEFAULT NULL COMMENT '乐观锁',
                            `create_by` bigint DEFAULT NULL COMMENT '创建人',
                            `create_time` datetime DEFAULT NULL COMMENT '创建时间',
                            `update_by` bigint DEFAULT NULL COMMENT '更新人',
                            `update_time` datetime DEFAULT NULL COMMENT '更新时间',
                            PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='部门信息';

2.编写实体类 和 Mapper 接口

  • 使用 @Table("sys_dept") 设置实体类与表名的映射关系

  • 使用 @Id(keyType = KeyType.Auto) 标识主键为自增

  • 使用 @Column(isLoginDelete = true) 标识字段为逻辑删除

  • 使用 @Column(onInsertValue = "now()") 标识字段为新增时添加默认值填充

  • 使用 @Column(onUpdateValue = "now()") 标识字段为更新时的默认值填充

  • 使用 @Column(version = true) 标识字段为乐观锁字段


@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("sys_dept")
public class SysDept implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id(keyType = KeyType.Auto)
    private Long id;
    
    private Long parentId;
    
    private String deptName;
    
    private String orderNum;
    
    // 逻辑删除
    @Column(isLogicDelete = true)
    private Boolean deleted;
    
    private Long createBy;
    
    // 默认填充
    @Column(onInsertValue = "now()")
    private LocalDateTime createTime;
    
    private Long updateBy;
    
    // 默认填充
    @Column(onUpdateValue = "now()")
    private LocalDateTime updateTime;
    
    // 版本号
    @Column(version = true)
    private Integer version;
}

SysUser 实体类 deptId 增加多对一注解【我们假定一个用户属于一个部门】:

@RelationManyToOne(selfField = "deptId", targetField = "id")
private Long deptId;

Mapper 接口继承 BaseMapper 接口:

public interface SysDeptMapper extends BaseMapper<SysDept> {

}

3.业务层接口和实现

业务接口继承 IService 接口:

public interface ISysDeptService extends IService<SysDept> {

}

业务接口实现继承 ServiceImpl 类:

@Service
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements ISysDeptService {

}

4.控制层

控制层方法调用实现联表查询:

@Autowired
private SysUserMapper sysUserMapper;

@GetMapping("/pageInfo")
public Page<UserInfo> pageInfo() {
    QueryWrapper queryWrapper = QueryWrapper.create()
            .select(SysUser::getId, SysUser::getDeptId, SysUser::getUsername, SysUser::getNickname)
            .leftJoin(SysDept.class)
            .on(SysDept::getId, SysUser::getDeptId)
            .select(SysDept::getDeptName)
            .orderBy(SysUser::getCreateTime).desc();
    return sysUserMapper.paginateWithRelationsAs(new Page<>(1, 10), queryWrapper, UserInfo.class);
}
    # 最终sql
    select 
      sys_user.id,
      sys_user.dept_id,
      sys_user.username,
      sys_user.nickname,
      sys_dept.dept_name
    from sys_user 
    left join sys_dept on sys_dept.id = sys_user.dept_id
    order by sys_user.create_time desc

    5.代码生成器

    官方文档:https://mybatis-flex.com/zh/others/codegen.html

    引入依赖及工具类

    <dependency>
        <groupId>com.mybatis-flex</groupId>
        <artifactId>mybatis-flex-codegen</artifactId>
        <version>1.11.0</version>
    </dependency>
    import com.mybatisflex.codegen.Generator;
    import com.mybatisflex.codegen.config.ColumnConfig;
    import com.mybatisflex.codegen.config.GlobalConfig;
    import com.zaxxer.hikari.HikariDataSource;
    
    /**
     * 代码生成工具类
     */
    public class CodegenUtil {
    
        public static void main(String[] args) {
            // 配置数据源
            HikariDataSource dataSource = new HikariDataSource();
            dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/db_mybatisflex?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
    
            // 创建配置内容,两种风格都可以。
            GlobalConfig globalConfig = createGlobalConfigUseStyle1();
    
            // 通过 datasource 和 globalConfig 创建代码生成器
            Generator generator = new Generator(dataSource, globalConfig);
    
            // 生成代码
            generator.generate();
        }
    
        /**
         * 常规风格
         */
        public static GlobalConfig createGlobalConfigUseStyle1() {
            // 创建配置内容
            GlobalConfig globalConfig = new GlobalConfig();
    
            // 设置路径及根包
            globalConfig.setSourceDir("D:/work/code/boot-business/src/main/java");
            globalConfig.setBasePackage("com.qiangesoft.mybatisflex.dmeo");
            globalConfig.setMapperXmlPath("D:/work/code/boot-business/src/main/resources/mapper");
    
            // 设置生成策略
    //        globalConfig.setTablePrefix("tb_");
            globalConfig.setGenerateTable("sys_user", "sys_dept");
            globalConfig.setUnGenerateTable("tb_test");
            globalConfig.setLogicDeleteColumn("deleted");
            globalConfig.setVersionColumn("version");
    
            // 设置生成 entity
            globalConfig.setEntityGenerateEnable(true);
            globalConfig.setEntityWithLombok(true);
            globalConfig.setEntityJdkVersion(17);
    
            // 设置生成 mapper
            globalConfig.setMapperGenerateEnable(true);
            globalConfig.setMapperAnnotation(true);
    
            // 设置生成 mapper.xml
            globalConfig.setMapperXmlGenerateEnable(true);
    
            // 设置生成 service
            globalConfig.setServiceGenerateEnable(true);
    
            // 设置生成 serviceImpl
            globalConfig.setServiceImplGenerateEnable(true);
    
            // 设置生成 controller
            globalConfig.setControllerGenerateEnable(true);
            globalConfig.setControllerRestStyle(true);
            globalConfig.setControllerRequestMappingPrefix("sys");
    
    
            // 单独配置某个列
            ColumnConfig columnConfig1 = new ColumnConfig();
            columnConfig1.setColumnName("create_time");
            columnConfig1.setOnInsertValue("now()");
    
            ColumnConfig columnConfig2 = new ColumnConfig();
            columnConfig2.setColumnName("update_time");
            columnConfig2.setOnUpdateValue("now()");
    
            globalConfig.setColumnConfig(columnConfig1);
            globalConfig.setColumnConfig(columnConfig2);
    
            // 指定表
            // globalConfig.setColumnConfig("tb_account", columnConfig);
    
            return globalConfig;
        }
    
        /**
         * 链式风格
         */
        public static GlobalConfig createGlobalConfigUseStyle2() {
            // 创建配置内容
            GlobalConfig globalConfig = new GlobalConfig();
    
            // 设置路径及根包
            globalConfig.getPackageConfig()
                    .setSourceDir("D:/work/code/boot-business/src/main/java")
                    .setBasePackage("com.qiangesoft.mybatisflex.demo")
                    .setMapperXmlPath("D:/work/code/boot-business/src/main/resources/mapper");
    
            // 设置生成策略
            globalConfig.getStrategyConfig()
    //                .setTablePrefix("sys_")
                    .setGenerateTable("sys_user", "sys_dept")
                    .setUnGenerateTable("tb_test")
                    .setIgnoreColumns("test")
                    .setLogicDeleteColumn("deleted")
                    .setVersionColumn("version");
    
            // 设置生成 entity
            globalConfig.enableEntity()
                    .setWithLombok(true)
                    .setAlwaysGenColumnAnnotation(true)
                    .setJdkVersion(17);
    
            // 设置生成 mapper
            globalConfig.enableMapper()
                    .setMapperAnnotation(true);
    
            // 配置生成 mapper.xml
            globalConfig.enableMapperXml();
    
            // 设置生成 service
            globalConfig.enableService();
    
            // 设置生成 serviceImpl
            globalConfig.enableServiceImpl();
    
            // 设置生成 controller
            globalConfig.enableController()
                    .setRestStyle(true)
                    .setRequestMappingPrefix("sys");
    
            // 单独配置某个列
            ColumnConfig columnConfig1 = new ColumnConfig();
            columnConfig1.setColumnName("create_time");
            columnConfig1.setOnInsertValue("now()");
    
            ColumnConfig columnConfig2 = new ColumnConfig();
            columnConfig2.setColumnName("update_time");
            columnConfig2.setOnUpdateValue("now()");
    
            globalConfig.getStrategyConfig()
                    .setColumnConfig(columnConfig1)
                    .setColumnConfig(columnConfig2);
    
            return globalConfig;
        }
    
    }

    六、总结

    MyBatis-Flex不是要替代MyBatis,而是让它变得更强大!如果你正在面临:
    🔹 动态SQL编写痛苦
    🔹 重复CRUD代码太多
    🔹 多表查询复杂难维护

    那么,MyBatis-Flex 绝对是你的不二之选!

    🚀 立即体验:
    官方文档:https://mybatis-flex.com/

    Gitee:https://gitee.com/mybatis-flex/mybatis-flex

    🚀 欢迎关注:

    关注底部微信公众号,可学习、源码及副业接单:

    • 回复【进群】,免费进副业接单群!
    • 回复【副业】,领取副业接单技巧及避坑指南!
    • 回复【java】,领取2025年Java学习路线!

    💬 互动话题:
    你在使用MyBatis时遇到过哪些痛点?欢迎留言讨论!

    评论 2
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    程序员Meteor

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值