- 1 MyBatis Flex
- 1.1 简介
- 1.2 简单操作
- 1.2.1 配置
- 1.2.1.1 pom.xml
- 1.2.1.2 yml配置
- 1.2.2 生成代码
- 1.2.3 业务类
- 1.2.3.1 实体
- 1.2.3.2 实体脱敏
- 1.2.3.3 Mapper接口
- 1.2.3.4 Service层
- 1.2.1 配置
- 1.3 MyBatis-Flex 核心 API
- 1.3.1 条件构造器(QueryWrapper)
- 1.3.2 更新操作(UpdateWrapper)
- 1.3.3 分页与聚合
- 1.3.4 事务管理
- 1.3.5 高级特性
1 MyBatis Flex
1.1 简介
MyBatis-Flex 是一个在 MyBatis 基础上深度增强的框架,专为解决 Java 开发中数据库操作的复杂性而设计。它不仅保留了 MyBatis 原生的灵活性,可以自由编写 SQL 语句,同时又引入了一系列强大的特性,大大简化了开发流程。
MyBatis-Flex 官网
教程学习地址:https://mybatis-flex.com/zh/intro/getting-started.html
与其他 ORM 框架相比,MyBatis-Flex 具有显著的优势。它非常轻量,除了 MyBatis 之外,没有任何第三方依赖,这不仅减少了项目的依赖复杂度,还提高了系统的稳定性和自主性。在性能方面,MyBatis-Flex 表现卓越,其独特的架构设计,在 SQL 执行过程中没有任何 SQL 解析操作,从而带来了指数级的性能增长 ,查询单条数据和查询 10 条数据的速度,大概是 MyBatis-Plus 的 5-10 倍。
和MyBatis-Plus 与 Fluent-Mybatis 对比:
| 功能或特点 | MyBatis-Flex | MyBatis-Plus | Fluent-Mybatis |
|---|---|---|---|
| 对 entity 的基本增删改查 | ✅ | ✅ | ✅ |
| 分页查询 | ✅ | ✅ | ✅ |
| 分页查询之总量缓存 | ✅ | ✅ | ❌ |
| 多表查询: from 多张表 | ✅ | ❌ | ❌ |
| 多表查询: left join、inner join 等等 | ✅ | ❌ | ✅ |
| 多表查询: union,union all | ✅ | ❌ | ✅ |
| 单主键配置 | ✅ | ✅ | ✅ |
| 多种 id 生成策略 | ✅ | ✅ | ✅ |
| 支持多主键、复合主键 | ✅ | ❌ | ❌ |
| 字段的 typeHandler 配置 | ✅ | ✅ | ✅ |
| 除了 Mybatis,无其他第三方依赖(更轻量) | ✅ | ❌ | ❌ |
| QueryWrapper 是否支持在微服务项目下进行 RPC 传输 | ✅ | ❌ | 未知 |
| 逻辑删除 | ✅ | ✅ | ✅ |
| 乐观锁 | ✅ | ✅ | ✅ |
| SQL 审计 | ✅ | ❌ | ❌ |
| 数据填充 | ✅ | ✔️ (收费) | ✅ |
| 数据脱敏 | ✅ | ✔️ (收费) | ❌ |
| 字段权限 | ✅ | ✔️ (收费) | ❌ |
| 字段加密 | ✅ | ✔️ (收费) | ❌ |
| 字典回写 | ✅ | ✔️ (收费) | ❌ |
| Db + Row | ✅ | ❌ | ❌ |
| Entity 监听 | ✅ | ❌ | ❌ |
| 多数据源支持 | ✅ | ✅(不支持非Spring项目) | ❌ |
| 多数据源是否支持 Spring 的事务管理,比如 @Transactional 和 TransactionTemplate 等 | ✅ | ❌ | ❌ |
| 多数据源是否支持 "非Spring" 项目 | ✅ | ❌ | ❌ |
| 多租户 | ✅ | ✅ | ❌ |
| 动态表名 | ✅ | ✅ | ❌ |
| 动态 Schema | ✅ | ❌ | ❌ |
1.2 简单操作
1.2.1 配置
1.2.1.1 pom.xml
SpringBoot v2.x 的场景下依赖
<!-- MyBatis-Flex 核心依赖 -->
<dependency><groupId>com.mybatis-flex</groupId><artifactId>mybatis-flex-spring-boot-starter</artifactId><version>1.11.4</version>
</dependency><!-- 数据库驱动(以 MySQL 为例) -->
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version>
</dependency><!-- 用于生成代码-->
<dependency><groupId>com.mybatis-flex</groupId><artifactId>mybatis-flex-codegen</artifactId><version>1.11.4</version>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.23</version>
</dependency>
SpringBoot v3.x,需要使用如下依赖:
<dependency><groupId>com.mybatis-flex</groupId><artifactId>mybatis-flex-spring-boot3-starter</artifactId><version>1.11.3</version>
</dependency>
1.2.1.2 yml配置
在application.yml文件中配置数据源
spring:datasource:url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTCusername: xxxxpassword: xxxxdriver-class-name: com.mysql.cj.jdbc.Driver# MyBatis-Flex 高级配置
mybatis-flex:# 是否打印 SQL(开发环境建议开启)print-sql: true# 全局配置global-config:# 逻辑删除配置logic-delete:logic-delete-value: 1 # 已删除值logic-not-delete-value: 0 # 未删除值# 多租户配置tenant-config:ignore-tables: sys_config, sys_log # 忽略租户过滤的表# 字段安全类型(脱敏/加密)column-security:# 全局加密密钥(可覆盖)aes-key: "my-secret-key-123"
1.2.2 生成代码
public static void main(String[] args) {//配置数据源DruidDataSource dataSource = new DruidDataSource();dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8");dataSource.setUsername("root");dataSource.setPassword("root");GlobalConfig globalConfig = createGlobalConfigUseStyle();//通过 datasource 和 globalConfig 创建代码生成器Generator generator = new Generator(dataSource, globalConfig);//生成代码generator.generate();}public static GlobalConfig createGlobalConfigUseStyle() {//创建配置内容GlobalConfig globalConfig = new GlobalConfig();String projectPath = AutoGen.class.getResource("/").getPath();String sourceDir = projectPath.substring(0, projectPath.indexOf("/target/")) ;globalConfig.setSourceDir(sourceDir);//设置根包globalConfig.getPackageConfig().setBasePackage("com.test").setMapperXmlPath(sourceDir+"/com/test/mapper");//设置表前缀和只生成哪些表,setGenerateTable 未配置时,生成所有表globalConfig.getStrategyConfig().setGenerateTable("user");//设置生成 entity 并启用 LombokglobalConfig.enableEntity().setWithLombok(true).setJdkVersion(17).setClassSuffix("Entity");//设置生成相关类及文件globalConfig.enableMapper();globalConfig.enableMapperXml();globalConfig.enableService();globalConfig.enableServiceImpl();globalConfig.enableController();//可以单独配置某个列/*ColumnConfig columnConfig = new ColumnConfig();columnConfig.setColumnName("tenant_id");columnConfig.setLarge(true);columnConfig.setVersion(true);globalConfig.getStrategyConfig().setColumnConfig("tb_account", columnConfig);*/return globalConfig;}
1.2.3 业务类
1.2.3.1 实体
@Table("tb_account")
@Data
public class Account {@Id(keyType = KeyType.Auto)private Long id;@Column("user_name")private String userName;private Integer age;@Column(onInsertValue = "now()")private LocalDateTime createTime;@Column(onUpdateValue = "now()")private LocalDateTime updateTime;// 逻辑删除字段(0:正常,1:删除)@Column(logicDelete = true)private Integer isDeleted;// 多租户字段@Column(tenantId = true)private Long tenantId;// 加密字段(手机号)@Column(cryptoType = CryptoType.AES)private String mobile;// 脱敏字段(银行卡号)@Column(maskType = MaskType.BANK_CARD)private String bankCard;// 乐观锁字段@Column(version = true)private Integer version;
}
@ColumnMask是 MyBatis-Flex 提供的内置脱敏注解,目前已经提供了9种脱敏规则,具体:手机号脱敏、固定电话脱敏、身份证号脱敏、车牌号脱敏、地址脱敏、邮件脱敏、密码脱敏、银行卡号脱敏
1.2.3.2 实体脱敏
当 Mybaits-Flex 内置的9种脱敏规则无法满足要求时,我们还可以自定义脱敏规则,其步骤如下:
通过 MaskManager 注册新的脱敏规则:
MaskManager.registerMaskProcessor("自定义规则名称", data -> {return data;})
使用自定义的脱敏规则
@Table("tb_account")
public class Account {@Id(keyType = KeyType.Auto)private Long id;@ColumnMask("自定义规则名称")private String userName;
}
1.2.3.3 Mapper接口
@Mapper
public interface AccountMapper extends BaseMapper<Account> {// 自定义SQL方法示例@Select("SELECT * FROM tb_account WHERE age > #{minAge}")List<Account> selectByMinAge(@Param("minAge") int minAge);
}
1.2.3.4 Service层
@Service
public class AccountService extends ServiceImpl<AccountMapper, Account> {// 自定义业务方法public List<Account> findAdults() {return queryChain().select(Account::getId, Account::getUserName).where(Account::getAge).ge(18).list();}
}
1.3 MyBatis-Flex 核心 API
1.3.1 条件构造器(QueryWrapper)
// 基础查询
QueryWrapper query = QueryWrapper.create().select(ACCOUNT.ID, ACCOUNT.USER_NAME, ACCOUNT.AGE).from(ACCOUNT).where(ACCOUNT.AGE.between(18, 60)).and(ACCOUNT.USER_NAME.like("张%")).orderBy(ACCOUNT.AGE.desc(), ACCOUNT.ID.asc()).limit(10);// 联表查询(带别名)
QueryWrapper query = QueryWrapper.create().select(ACCOUNT.ID, ORDER.ORDER_NO, ORDER.AMOUNT).from(ACCOUNT.as("a")).leftJoin(ORDER).as("o").on(ACCOUNT.ID.eq(ORDER.ACCOUNT_ID)).where(ORDER.CREATE_TIME.ge(LocalDate.now().minusMonths(1))).groupBy(ACCOUNT.ID).having(sum(ORDER.AMOUNT).gt(10000));// 子查询
QueryWrapper subQuery = QueryWrapper.create().select(ORDER.ACCOUNT_ID).from(ORDER).where(ORDER.STATUS.eq(1));QueryWrapper mainQuery = QueryWrapper.create().select().from(ACCOUNT).where(ACCOUNT.ID.in(subQuery)).and(ACCOUNT.TENANT_ID.eq(123));// Lambda 表达式
LambdaQueryWrapper<Account> lambdaQuery = LambdaQueryWrapper.create().select(Account::getId, Account::getUserName).eq(Account::getAge, 25).likeRight(Account::getUserName, "张").orderByDesc(Account::getCreateTime);
1.3.2 更新操作(UpdateWrapper)
// 条件更新
UpdateWrapper update = UpdateWrapper.create().set(ACCOUNT.AGE, ACCOUNT.AGE.add(1)).set(ACCOUNT.UPDATE_TIME, LocalDateTime.now()).where(ACCOUNT.LAST_LOGIN_TIME.lt(LocalDate.now().minusYears(1)));// 实体更新
Account account = new Account();
account.setStatus(2);
UpdateWrapper update = UpdateWrapper.of(account).where(ACCOUNT.STATUS.eq(1).and(ACCOUNT.AGE.lt(18)));// Lambda 更新
LambdaUpdateWrapper<Account> lambdaUpdate = LambdaUpdateWrapper.create().set(Account::getAge, 30).set(Account::getUpdateTime, LocalDateTime.now()).eq(Account::getId, 1001);
1.3.3 分页与聚合
// 分页查询
Page<Account> page = Page.of(1, 20); // 第1页,每页20条
QueryWrapper query = QueryWrapper.create().where(ACCOUNT.AGE.ge(18)).orderBy(ACCOUNT.CREATE_TIME.desc());Page<Account> result = mapper.paginate(page, query);// 分页结果处理
List<Account> records = result.getRecords();
long total = result.getTotalRow();
long totalPages = result.getTotalPage();// 聚合查询
QueryWrapper aggQuery = QueryWrapper.create().select(ACCOUNT.DEPT_ID, count().as("emp_count"),avg(ACCOUNT.SALARY).as("avg_salary"),max(ACCOUNT.SALARY).as("max_salary")).groupBy(ACCOUNT.DEPT_ID).having(avg(ACCOUNT.SALARY).gt(10000));
1.3.4 事务管理
// 声明式事务
@Transactional(rollbackFor = Exception.class)
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {// 扣减转出账户UpdateWrapper deduct = UpdateWrapper.create().setRaw(ACCOUNT.BALANCE, "balance - ?", amount).where(ACCOUNT.ID.eq(fromId));accountMapper.updateByQuery(deduct);// 增加转入账户UpdateWrapper add = UpdateWrapper.create().setRaw(ACCOUNT.BALANCE, "balance + ?", amount).where(ACCOUNT.ID.eq(toId));accountMapper.updateByQuery(add);// 记录交易流水transactionService.logTransfer(fromId, toId, amount);
}// 编程式事务
public void batchImport(List<Account> accounts) {Transaction.tx(() -> {for (Account account : accounts) {if (account.getAge() < 18) {throw new RuntimeException("未成年账户禁止导入");}accountMapper.insert(account);}return true;});
}
1.3.5 高级特性
// 1. 逻辑删除(自动添加条件)
accountMapper.deleteById(1L); // → UPDATE SET is_deleted=1 WHERE id=1// 2. 多租户过滤(自动添加租户ID条件)
List<Account> list = accountMapper.selectAll();
// → SELECT * FROM tb_account WHERE tenant_id=当前租户ID// 3. 字段加密/解密(自动处理)
Account account = accountMapper.selectOneById(1L);
System.out.println(account.getMobile()); // 自动解密 → 13800138000// 4. 数据脱敏
Account account = accountMapper.selectOneById(1L);
System.out.println(account.getBankCard()); // → 622202******1234// 5. 乐观锁更新
Account account = accountMapper.selectOneById(1L);
account.setBalance(account.getBalance() + 100);
accountMapper.update(account);
// → UPDATE ... WHERE id=1 AND version=旧版本
6、工具类使用// Db 工具类快速操作
Db.insert("account", "id,user_name,age", 1001, "张三", 30);
Db.updateById("account", "age", 31, 1001);
List<Account> list = Db.selectAllByCondition(Account.class, "age > ?", 18);// 批量操作
List<Account> accounts = ...;
Db.executeBatch(accounts, 1000, (mapper, account) -> {mapper.insert(account);
});// SQL 工具
String inSql = SqlUtil.buildInCondition("id", Arrays.asList(1,2,3));
// → id IN (1,2,3)String safeSql = SqlUtil.escapeSql("SELECT * FROM user WHERE name='admin' OR 1=1");
// → 防止 SQL 注入的安全处理