引言
如果你是一名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 的事务管理,比如 |
✅ |
❌ |
❌ |
多数据源是否支持 "非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时遇到过哪些痛点?欢迎留言讨论!