一. MyBatis-Flex 到底是啥?凭啥用它?
想象一下 MyBatis 的灵活 + APT,揉在一起,就是 MyBatis-Flex!它的核心卖点:
-
无 XML!无 XML!无 XML! 重要的事情说三遍!所有 SQL 动态生成都在 Java 代码里搞定,告别繁琐的 XML 配置和手写 SQL 语句(当然,复杂 SQL 想写也支持)。
-
超灵活的 QueryWrapper: 基于 Lambda 表达式构建查询条件,链式调用,类型安全,代码即 SQL,IDE 智能提示爽歪歪。再也不用怕字段名拼错了!
-
极致性能: APT(Annotation Processing Tool)代码生成 + 精心优化的 SQL 生成逻辑,性能杠杠的,尤其在大数据量、复杂查询场景下优势明显。
-
强大的多表支持: 关联查询 (
1v1,1vN,NvN) 配置简单,查询高效,支持JOIN和子查询两种模式。 -
丰富的功能: 逻辑删除、多租户、字段权限控制(数据脱敏)、乐观锁、数据填充... 该有的都有。
-
轻量级: 核心依赖极小,只依赖 MyBatis,启动快,内存占用低。
-
开发者:阿里巴巴、腾讯、科大讯飞、 网宿科技、新东方 等企业的同学+学生
一句话总结: 用更现代、更优雅、更高效的方式玩转 MyBatis!
二、Spring Boot 3 + MyBatis-Flex:光速启动!
2.1 添加pom.xml 依赖.
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot3-starter</artifactId>
<version>1.10.9</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
<!-- 生成APT 代码的关键-->
<path>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>1.10.9</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
2.2 application,yml 配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/flex_test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
2.3 创建实体类 (Account.java)
注意 @Table 和 @Column 注解! APT 靠它们生成代码
@Data
@Table("tb_account") // 指定表名
public class Account {
@Id(keyType = KeyType.Auto) // 主键,自增
private Long id;
@Column("user_name") // 指定列名
@ColumnMask(Masks.CHINESE_NAME) // 数据脱敏
private String userName;
private Integer age;
private Date birthday;
private BigDecimal balance;
}
审计sql日志:
@Configuration
public class MyBatisFlexConfiguration {
private static final Logger logger = LoggerFactory
.getLogger("mybatis-flex-sql");
public MyBatisFlexConfiguration() {
//开启审计功能
AuditManager.setAuditEnable(true);
//设置 SQL 审计收集器
AuditManager.setMessageCollector(auditMessage ->
logger.info("{},{}ms", auditMessage.getFullSql()
, auditMessage.getElapsedTime())
);
}
}
2.4 创建 Mapper 接口 (AccountMapper.java)
public interface AccountMapper extends BaseMapper<Account> {
// 这里可以定义自己的复杂查询方法,后面会讲到
}
2.5 启动类
@SpringBootApplication
@MapperScan("com.yourpackage.mapper") // 扫描你的 Mapper 接口
public class FlexDemoApplication {
public static void main(String[] args) {
SpringApplication.run(FlexDemoApplication.class, args);
}
}
三. 基础 CRUD:简单到哭!
得益于 BaseMapper,基本的增删改查直接起飞 🛫。
@Autowired
private AccountMapper accountMapper;
// 插入 (返回主键会自动填充到 entity.id)
accountMapper.insert(account);
// 根据 ID 查询
Account account = accountMapper.selectOneById(1L);
// 更新 (根据主键)
account.setUserName("NewName");
accountMapper.update(account);
// 删除 (根据主键)
accountMapper.deleteById(1L);
// 查询所有
List<Account> accounts = accountMapper.selectAll();
3.1. QueryWrapper:Lambda 链式编程,优雅永不过时!
这才是 Flex 的灵魂所在! 用 Lambda 表达式构建查询条件,类型安全,IDE 友好。
// 导入静态方法方便链式调用
import static com.mybatisflex.core.query.QueryMethods.*;
// 查询年龄大于18,用户名包含"Tom",按余额降序
QueryWrapper query = QueryWrapper.create()
.select() // 查询所有列
.from(ACCOUNT) // 使用静态导入的 ACCOUNT 对象 (由APT生成)
.where(ACCOUNT.AGE.ge(18)) // age >= 18
.and(ACCOUNT.USER_NAME.like("Tom%")) // AND user_name LIKE 'Tom%'
.orderBy(ACCOUNT.BALANCE.desc()); // ORDER BY balance DESC
List<Account> accounts = accountMapper.selectListByQuery(query);
看这段代码!
-
ACCOUNT是一个由 APT 自动生成的 静态常量对象,对应tb_account表及其所有字段。 -
ACCOUNT.AGE.ge(18)->age >= 18 -
ACCOUNT.USER_NAME.like("Tom%")->user_name LIKE 'Tom%' -
链式调用,清晰明了。
-
完全类型安全!
AGE是数字类型,USER_NAME是字符串类型。写ACCOUNT.AGE.like("Tom")?编译直接报错!告别字段名拼写错误! -
IDE 智能提示爽到飞起!
query.where(ACCOUNT.ID.in(1, 2, 3)); // IN
query.where(ACCOUNT.BIRTHDAY.between(startDate, endDate)); // BETWEEN
query.where(ACCOUNT.BALANCE.isNotNull()); // IS NOT NULL
query.groupBy(ACCOUNT.AGE); // GROUP BY
query.having(sum(ACCOUNT.BALANCE).gt(10000)); // HAVING
动态SQL 也超级简单
QueryWrapper query = QueryWrapper.create()
.select().from(ACCOUNT);
if (age != null) {
query.and(ACCOUNT.AGE.eq(age));
}
if (StringUtils.isNotBlank(userName)) {
query.and(ACCOUNT.USER_NAME.like(userName + "%"));
}
3.2. 分页:一行代码搞定,性能还贼强!
Flex 的分页设计非常高效,内置支持多种数据库分页语法,自动优化 COUNT 查询。
// 第2页,每页10条
Page<Account> page = Page.of(2, 10);
// 构建查询条件
QueryWrapper query = QueryWrapper.create()
.where(ACCOUNT.AGE.gt(20));
// 执行分页查询 (关键方法: paginateAs)
Page<Account> result = accountMapper.paginateAs(page, query, Account.class);
// 结果
System.out.println("总记录数: " + result.getTotalRow());
System.out.println("总页数: " + result.getTotalPage());
List<Account> records = result.getRecords(); // 当前页数据
paginateAs 这一行代码就搞定了分页查询和总记录数查询!
性能优化点: 对于复杂的查询,Flex 会智能判断是否需要对原查询进行优化(例如去掉不必要的 ORDER BY)来执行 COUNT 查询,提升性能。对于简单的 WHERE 条件,COUNT 查询就是 SELECT COUNT(*) FROM ...。
3.3. Relations:关联查询,告别 N+1!
查询 Account 时想同时查出它关联的 Order 列表?Flex 的关联注解轻松搞定。
定义关联关系 (Account.java)
// 查询 Account 及其关联的 Orders (使用 Relations QueryWrapper)
QueryWrapper query = QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.eq(1L))
.withRelations(); // 关键:加载关联关系
// 执行查询 (使用 selectOneWithRelationsByQuery)
Account accountWithOrders = accountMapper.selectOneWithRelationsByQuery(query);
// 直接访问关联的订单
List<Order> orders = accountWithOrders.getOrders(); // 数据已加载好!
原理(两种模式):
-
JOIN模式 (默认): 生成一条包含LEFT JOIN的大 SQL 一次性查出所有数据。Flex 会自动处理结果集映射,将关联数据组装到主对象的集合属性中。性能好,推荐使用。 -
子查询模式: 先查询主对象,再根据主对象 ID 批量查询关联对象(SELECT * FROM order WHERE account_id IN (?, ?, ...))。避免 N+1 问题。可以通过@RelationXXX(extraCondition = ...)或全局配置切换。
四. 🔥 深入核心:SQL Provider 原理揭秘
大家有没有好奇,Flex 不用 XML,那些
QueryWrapper对象是怎么变成最终执行的 SQL 语句的?秘密就在于SQL Provider和APT!
4.1 核心参与者
-
QueryWrapper: 你写的查询条件构建器,包含了要查询的表、字段、条件、排序等信息。它是一个 结构化对象。 -
BaseMapper方法 (如selectListByQuery): 这些方法需要一个 SQL 语句来执行。它们内部依赖于@SelectProvider注解。 -
@SelectProvider/@UpdateProvider等注解: MyBatis 提供的机制,允许你指定一个类和方法来 动态生成 SQL 字符串。 -
SqlProvider类 (TableDefSqlProvider): Flex 提供的一个核心类。它的方法(如selectByQuery)接收QueryWrapper对象作为参数。 -
QueryWrapper的toSQL()方法: 这是关键!QueryWrapper对象有能力将自己“翻译”成标准的 SQL 字符串及其参数。 -
TableDef(由 APT 生成): 就是你看到的ACCOUNT那个类。它包含了表名、所有字段名(字符串常量)、所有字段对应的QueryColumn对象(用于构建条件)。APT 在编译期间根据实体类的@Table和@Column注解自动生成这些类。这是类型安全和 IDE 提示的基础!
4.2 执行流程

4.3 APT 的核心作用

-
编译期生成 (
AccountTableDef): APT 在编译时扫描Account类上的@Table和@Column注解,生成对应的AccountTableDef类。这个类包含:-
表名的字符串常量 (
TABLE_NAME)。 -
代表表本身的
QueryTable对象 (TABLE)。 -
代表表中每个字段的
QueryColumn对象 (ID,USER_NAME等)。这些对象知道自己属于哪个表、叫什么名字、是什么类型。
-
-
运行时构建 (
QueryWrapper): 你在代码中使用ACCOUNT.ID.eq(1):-
ACCOUNT就是AccountTableDef.TABLE(一个QueryTable)。 -
ID是AccountTableDef.ID(一个QueryColumn)。 -
eq(1)方法调用QueryColumn的方法,返回一个QueryCondition对象。这个对象内部记录了字段名(ID)、操作符(=)、值(1)。
-
-
SQL 生成 (
toSQL()):QueryWrapper对象内部维护了要查询的表 (FROM)、选择的列 (SELECT)、条件 (WHERE)、排序 (ORDER BY) 等信息(这些信息就是由你链式调用添加进去的各种QueryTable,QueryColumn,QueryCondition对象组成的结构)。当调用toSQL()时,QueryWrapper会遍历这个结构,按照 SQL 语法规则,将这些对象“翻译”成对应的 SQL 片段(字符串),并收集参数值。最终拼接成完整的、可执行的 SQL 语句。
总结 SQL Provider 原理: 利用 MyBatis 的 Provider 机制,将结构化的 QueryWrapper 对象传递给一个专门的 SqlProvider 类。SqlProvider 调用 QueryWrapper 的 toSQL() 方法,该方法利用 APT 生成的元数据 (TableDef 和 QueryColumn) 将查询意图动态地、安全地(类型安全、字段名正确)翻译成 SQL 字符串和参数列表。这个过程完全在运行时由 Java 代码完成,不依赖 XML。
五. 小结:MyBatis-Flex 香在哪?
-
开发效率爆炸: Lambda 链式 Wrapper + APT 生成 + 无 XML,写代码行云流水,减少错误。
-
代码可读性极强: Java 代码清晰表达查询意图,维护方便。
-
性能优异: 精心设计的 SQL 生成逻辑,优化过的分页,避免 N+1。
-
功能全面: CRUD、分页、关联、逻辑删、多租户、乐观锁,数据脱敏,字段加密,动态数据源,动态表,多数据源,读写分离. 开箱即用。
-
拥抱新特性: 深度集成 Lambda 表达式和 APT,现代化开发体验。
-
轻量灵活: 核心精简,易于集成和理解。
如果你受够了 XML 的繁琐,又觉得其他 ORM 的 Wrapper 不够优雅或性能有顾虑,MyBatis-Flex 绝对值得你一试!
它让你用更愉悦的方式,写出更高效、更健壮的数据库访问代码。赶紧上手体验一下吧!🚀

7085

被折叠的 条评论
为什么被折叠?



