外观
MyBatis-Flex 笔记
约 2209 字大约 7 分钟
2025-08-16
一、MyBatis-Flex 简介
1.1 什么是 MyBatis-Flex?
MyBatis-Flex 是一个基于 MyBatis 的增强框架,它在不改变 MyBatis 核心功能的前提下,提供了更简洁、更强大的 API 和功能。MyBatis-Flex 旨在简化开发、提高效率,同时保持对 MyBatis 的完全兼容性。
1.2 MyBatis-Flex 核心特点
- 无侵入性:只做增强不做改变,不影响现有工程
- 性能优秀:启动即自动注入基础功能,性能损耗极小
- 强大的查询构建器:提供更灵活、更强大的条件构建方式
- 多表查询支持:内置多表联查功能,无需复杂配置
- 动态表名支持:支持运行时动态切换表名
- 逻辑删除:内置逻辑删除功能
- 乐观锁:支持乐观锁机制
- 代码生成器:快速生成实体类、Mapper 等代码
- 高度可定制:支持自定义 SQL 模板、拦截器等
1.3 与 MyBatis 和 MyBatis-Plus 比较
| 特性 | MyBatis | MyBatis-Plus | MyBatis-Flex |
|---|---|---|---|
| SQL 编写 | 需要手动编写 | 基础 CRUD 无需编写 | 基础 CRUD 无需编写 |
| 条件构造器 | 无 | 有 | 更强大、更灵活 |
| 多表查询 | 需手动实现 | 需要额外配置 | 内置支持 |
| 动态表名 | 无 | 有(需配置) | 内置支持 |
| 分页查询 | 需手动实现 | 内置分页插件 | 内置分页插件 |
| 代码生成器 | 无 | 有 | 有 |
| Lambda 表达式 | 不支持 | 支持 | 支持 |
MyBatis-Flex 的独特优势:
- 更强大的多表查询能力
- 更灵活的查询条件构建
- 更简洁的 API 设计
- 更好的性能表现
- 更丰富的内置功能
二、环境搭建
2.1 依赖配置
Maven 配置:
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
<version>1.6.3</version>
</dependency>Gradle 配置:
implementation 'com.mybatis-flex:mybatis-flex-spring-boot-starter:1.6.3'2.2 基础配置
application.yml 配置:
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-flex:
# 开启驼峰命名转换
configuration:
map-underscore-to-camel-case: true
# 全局配置
global-config:
db-config:
id-type: auto # 主键生成策略2.3 启动类配置
@SpringBootApplication
@MapperScan("com.example.mapper") // 扫描 Mapper 接口
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}三、基础 CRUD 操作
3.1 实体类定义
@Data
@Table("t_user") // 指定表名
public class User {
@Id(keyType = KeyType.Auto) // 主键策略
private Long id;
@Column("name") // 指定字段名
private String username;
private Integer age;
private String email;
}3.2 Mapper 接口
public interface UserMapper extends MyBaseMapper<User> {
// 继承 MyBaseMapper,自动获得 CRUD 方法
}3.3 基本 CRUD 使用
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 插入
public int insertUser() {
User user = new User();
user.setUsername("John");
user.setAge(25);
user.setEmail("john@example.com");
return userMapper.insert(user);
}
// 查询
public User selectById(Long id) {
return userMapper.selectById(id);
}
public List<User> selectAll() {
return userMapper.selectAll();
}
// 更新
public int updateUser() {
User user = new User();
user.setId(1L);
user.setAge(26);
return userMapper.updateById(user);
}
// 删除
public int deleteUser(Long id) {
return userMapper.deleteById(id);
}
}四、查询条件构建
4.1 QueryWrapper
MyBatis-Flex 提供了强大的 QueryWrapper 用于构建查询条件:
// 查询 name='John' 且 age>20 的用户
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(USER)
.where(USER.NAME.eq("John"))
.and(USER.AGE.gt(20));
List<User> users = userMapper.selectListByQuery(queryWrapper);
// 查询 name 包含 'o',age 在 20 到 30 之间,且 email 不为空的用户,按 age 降序
queryWrapper = QueryWrapper.create()
.select()
.from(USER)
.where(USER.NAME.like("o"))
.and(USER.AGE.between(20, 30))
.and(USER.EMAIL.isNotNull())
.orderBy(USER.AGE.desc());
users = userMapper.selectListByQuery(queryWrapper);4.2 UpdateWrapper
用于更新操作:
// 将 name='John' 的用户 age 更新为 26
UpdateWrapper updateWrapper = UpdateWrapper.create()
.update(USER)
.set(USER.AGE, 26)
.where(USER.NAME.eq("John"));
userMapper.updateByCondition(updateWrapper);
// 将 age<25 的用户 is_deleted 设置为 1(逻辑删除)
UpdateWrapper updateWrapper = UpdateWrapper.create()
.update(USER)
.set(USER.IS_DELETED, 1)
.where(USER.AGE.lt(25));
userMapper.updateByCondition(updateWrapper);4.3 常用查询条件
| 方法 | 说明 | 示例 |
|---|---|---|
eq | 等于 | USER.NAME.eq("John") |
ne | 不等于 | USER.AGE.ne(20) |
gt | 大于 | USER.AGE.gt(20) |
ge | 大于等于 | USER.AGE.ge(20) |
lt | 小于 | USER.AGE.lt(30) |
le | 小于等于 | USER.AGE.le(30) |
like | 模糊查询 | USER.NAME.like("o") |
between | 区间查询 | USER.AGE.between(20, 30) |
in | IN 查询 | USER.AGE.in(20, 25, 30) |
isNull | 为空 | USER.EMAIL.isNull() |
isNotNull | 不为空 | USER.EMAIL.isNotNull() |
orderByAsc | 升序排序 | orderBy(USER.AGE.asc()) |
orderByDesc | 降序排序 | orderBy(USER.AGE.desc()) |
五、多表查询
5.1 多表查询基础
MyBatis-Flex 内置了强大的多表查询功能,无需额外配置:
// 定义表对象
Table USER = Table.of("t_user");
Table ORDER = Table.of("t_order");
// 多表查询
QueryWrapper queryWrapper = QueryWrapper.create()
.select(USER.ALL_COLUMNS, ORDER.ALL_COLUMNS)
.from(USER)
.leftJoin(ORDER).on(USER.ID.eq(ORDER.USER_ID))
.where(USER.AGE.gt(20))
.and(ORDER.AMOUNT.gt(100))
.orderBy(USER.ID.desc());
List<UserOrderVO> userOrderList = userMapper.selectListByQueryAs(queryWrapper, UserOrderVO.class);5.2 多表查询示例
// 定义表对象
Table user = Table.of("t_user");
Table order = Table.of("t_order");
Table product = Table.of("t_product");
// 复杂多表查询
QueryWrapper queryWrapper = QueryWrapper.create()
.select(
user.selects("id", "name", "age"),
order.selects("id as order_id", "amount"),
product.selects("name as product_name")
)
.from(user)
.leftJoin(order).on(user.as("u").id().eq(order.as("o").user_id()))
.leftJoin(product).on(order.as("o").product_id().eq(product.as("p").id()))
.where(user.as("u").age().gt(18))
.and(order.as("o").amount().gt(100))
.and(product.as("p").price().gt(50))
.groupBy(user.as("u").id())
.having(order.as("o").amount().sum().gt(500))
.orderBy(user.as("u").id().desc())
.limit(0, 10);
List<UserOrderProductVO> result = userMapper.selectListByQueryAs(queryWrapper, UserOrderProductVO.class);5.3 多表查询结果映射
// 结果映射 VO 类
@Data
public class UserOrderProductVO {
private Long id;
private String name;
private Integer age;
private Long orderId;
private BigDecimal amount;
private String productName;
}
// 使用 QueryWrapper 进行多表查询并映射到 VO
List<UserOrderProductVO> result = userMapper.selectListByQueryAs(queryWrapper, UserOrderProductVO.class);六、分页查询
6.1 分页插件配置
@Configuration
public class MybatisFlexConfig {
@Bean
public MybatisFlexInterceptor mybatisFlexInterceptor() {
MybatisFlexInterceptor interceptor = new MybatisFlexInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}6.2 分页查询使用
// 第1页,每页5条记录
Page<User> page = new Page<>(1, 5);
// 执行分页查询
IPage<User> userPage = userMapper.paginate(page, QueryWrapper.create().where(USER.AGE.gt(20)));
// 获取分页信息
System.out.println("当前页: " + userPage.getCurrent());
System.out.println("每页条数: " + userPage.getSize());
System.out.println("总条数: " + userPage.getTotal());
System.out.println("总页数: " + userPage.getPages());
System.out.println("数据: " + userPage.getRecords());
// 带条件的分页查询
QueryWrapper queryWrapper = QueryWrapper.create().where(USER.AGE.gt(20));
userPage = userMapper.paginate(page, queryWrapper);七、高级功能
7.1 逻辑删除
配置:
mybatis-flex:
global-config:
db-config:
logic-delete-field: is_deleted # 逻辑删除字段
logic-not-delete-value: 0 # 逻辑未删除值
logic-delete-value: 1 # 逻辑已删除值实体类:
@Data
@Table("t_user")
public class User {
@Id(keyType = KeyType.Auto)
private Long id;
private String name;
private Integer age;
@IsDelete
private Integer isDeleted;
}使用:
// 删除操作会自动转换为更新操作
userMapper.deleteById(1L);
// 实际执行:UPDATE t_user SET is_deleted=1 WHERE id=1 AND is_deleted=0
// 查询操作会自动过滤已删除数据
userMapper.selectById(1L);
// 实际执行:SELECT * FROM t_user WHERE id=1 AND is_deleted=07.2 乐观锁
配置:
@Configuration
public class MybatisFlexConfig {
@Bean
public MybatisFlexInterceptor mybatisFlexInterceptor() {
MybatisFlexInterceptor interceptor = new MybatisFlexInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockInnerInterceptor());
return interceptor;
}
}实体类:
@Data
@Table("t_user")
public class User {
@Id(keyType = KeyType.Auto)
private Long id;
private String name;
private Integer age;
@Version
private Integer version;
}使用:
// 1. 查询数据
User user = userMapper.selectById(1L);
// 2. 更新数据
user.setAge(26);
userMapper.updateById(user);
// 生成的SQL:UPDATE t_user SET age=26, version=version+1 WHERE id=1 AND version=原来的version7.3 动态表名
实现动态表名处理器:
@Component
public class DynamicTableNameProcessor implements TableNameProcessor {
private static final ThreadLocal<String> TABLE_NAME_HOLDER = new ThreadLocal<>();
public static void setTableName(String tableName) {
TABLE_NAME_HOLDER.set(tableName);
}
@Override
public String process(String tableName, String sql, Object[] args) {
String dynamicTableName = TABLE_NAME_HOLDER.get();
return dynamicTableName != null ? dynamicTableName : tableName;
}
}配置:
@Configuration
public class MybatisFlexConfig {
@Bean
public MybatisFlexInterceptor mybatisFlexInterceptor() {
MybatisFlexInterceptor interceptor = new MybatisFlexInterceptor();
interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor());
return interceptor;
}
}使用:
// 设置动态表名
DynamicTableNameProcessor.setTableName("t_user_2023");
// 执行查询
List<User> users = userMapper.selectAll();
// 实际执行:SELECT * FROM t_user_2023八、代码生成器
8.1 依赖配置
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-codegen</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>8.2 代码生成器配置
public class CodeGenerator {
public static void main(String[] args) {
// 1. 创建数据源
DataSource dataSource = DbUtil.createDataSource(
"jdbc:mysql://localhost:3306/mydb",
"root",
"123456"
);
// 2. 创建代码生成器
FastAutoGenerator.create(dataSource)
// 3. 全局配置
.globalConfig(builder -> {
builder
.author("YourName") // 设置作者
.fileOverride() // 覆盖已生成文件
.outputDir(System.getProperty("user.dir") + "/src/main/java"); // 指定输出目录
})
// 4. 包配置
.packageConfig(builder -> {
builder
.parent("com.example") // 设置父包名
.moduleName("demo") // 设置模块名
.entity("entity") // 设置实体类包名
.mapper("mapper") // 设置Mapper包名
.service("service") // 设置Service包名
.serviceImpl("service.impl") // 设置ServiceImpl包名
.controller("controller"); // 设置Controller包名
})
// 5. 策略配置
.strategyConfig(builder -> {
builder
.entityBuilder()
.enableLombok() // 开启 Lombok
.logicDeleteColumnName("is_deleted") // 逻辑删除字段名
.naming(NamingStrategy.underline_to_camel) // 数据库表映射到实体的命名策略
.columnNaming(NamingStrategy.underline_to_camel) // 数据库表字段映射到实体的命名策略
.addTableFills(new Column("create_time", FieldFill.INSERT)) // 添加表字段填充
.addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE))
.idType(IdType.AUTO) // ID生成策略
.controllerBuilder()
.enableRestStyle() // 开启 REST 风格
.serviceBuilder()
.formatServiceFileName("%sService") // 格式化 service 接口文件名称
.formatServiceImplFileName("%sServiceImpl") // 格式化 service 实现类文件名称
.mapperBuilder()
.enableMapperAnnotation() // 开启 @Mapper 注解
.build();
})
// 6. 执行生成
.execute();
}
}九、最佳实践
9.1 实体类设计
@Data
@Table("t_user")
public class User {
@Id(keyType = KeyType.Auto)
private Long id;
@Column(value = "user_name", condition = SqlCondition.LIKE)
private String username;
@Transient
private String fullName; // 非表字段
@IsDelete
private Integer isDeleted;
@Version
private Integer version; // 乐观锁版本号
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}9.2 事务管理
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private OrderMapper orderMapper;
@Transactional
public void createUserAndOrder(User user, Order order) {
userMapper.insert(user);
order.setUserId(user.getId());
orderMapper.insert(order);
}
}9.3 常见问题解决
问题1:字段名与数据库不一致
解决方案:
@Column("user_name")
private String username;问题2:查询结果为空
解决方案:
- 检查表名、字段名是否正确
- 检查数据库连接是否正确
- 使用
@Transient标记非表字段
问题3:分页查询无效
解决方案:
- 确保已配置分页插件
- 确保使用
paginate方法
