MyBatis-Flex 全面解析与实战教程:轻量高效的 MyBatis 增强方案

一、MyBatis-Flex 简介

1.1 什么是 MyBatis-Flex

MyBatis-Flex 是一款基于 MyBatis 的轻量级增强框架,旨在简化数据库操作、提升开发效率,同时保留 MyBatis 原生灵活性与高性能优势。它并非替代 MyBatis,而是通过增强 API、优化架构设计,解决 MyBatis 单表操作繁琐、查询条件构建复杂等问题,且全程无第三方依赖,稳定性与自主性更强。

与同类框架相比,MyBatis-Flex 以"轻量、灵活、高性能"为核心定位:无 MyBatis 拦截器、无 SQL 解析环节,性能较传统增强框架提升 5-10 倍以上;同时支持多表查询、逻辑删除、数据脱敏等企业级特性,兼顾快速开发与复杂业务场景需求。

1.2 与 MyBatis/MyBatis-Plus 的区别

特性 MyBatis MyBatis-Plus MyBatis-Flex
核心定位 原生 ORM,侧重 SQL 与代码分离 MyBatis 增强,功能全面 轻量增强,兼顾性能与灵活
第三方依赖 依赖 hutool 等工具包 仅依赖 MyBatis,无其他依赖
性能表现 原生高效 拦截器模式有性能损耗 无拦截器、无 SQL 解析,性能最优
特色功能 原生 SQL 控制 代码生成、条件构造器 字段权限、数据脱敏、多租户原生支持

二、环境搭建(Spring Boot 集成)

本节以 Spring Boot 2.x + MySQL 为例,手把手教你完成 MyBatis-Flex 集成,Spring Boot 3.x 仅需调整依赖名称,操作一致。

2.1 前提条件

  • JDK 8+(Spring Boot 3.x 需 JDK 17+)

  • 熟悉 Spring Boot 基础配置与 Maven/Gradle 构建工具

  • 已安装 MySQL 数据库(5.7+)

2.2 创建数据库表

首先创建测试表 tb_account,并插入测试数据:

sql 复制代码
CREATE TABLE IF NOT EXISTS `tb_account` (
  `id` INTEGER PRIMARY KEY auto_increment COMMENT '主键ID',
  `user_name` VARCHAR(100) NOT NULL COMMENT '用户名',
  `age` INTEGER COMMENT '年龄',
  `birthday` DATETIME COMMENT '生日',
  `is_deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除标识(0-未删,1-已删)',
  `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) COMMENT '用户账户表';

INSERT INTO tb_account(user_name, age, birthday)
VALUES ('张三', 18, '2020-01-11'),
       ('李四', 19, '2021-03-21'),
       ('王五', 22, '2019-07-05');

2.3 添加 Maven 依赖

在 Spring Boot 项目的pom.xml 中添加以下依赖:

xml 复制代码
<dependencies/>
  <!-- MyBatis-Flex Spring Boot 启动器 -->
  <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>
    <scope>runtime</scope>
  </dependency/>

  <!-- 数据库连接池(HikariCP,Spring Boot 默认) -->
  <dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
  </dependency/>

  <!-- Lombok 简化实体类(可选,推荐) -->
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
  </dependency/>

  <!-- 测试依赖 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

提示:若使用 Spring Boot 3.x,需将 mybatis-flex-spring-boot-starter 替换为 mybatis-flex-spring-boot3-starter

2.4 配置数据源与 MyBatis-Flex

application.yml 中配置数据源和 MyBatis-Flex 基础参数:

yaml 复制代码
spring:
  # 数据源配置
  datasource:
    url: jdbc:mysql://localhost:3306/flex_test?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    username: root
    password: 12345678
    driver-class-name: com.mysql.cj.jdbc.Driver

# MyBatis-Flex 配置(可选,默认已适配大部分场景)
mybatis-flex:
  # 实体类扫描路径
  entity-scan: com.mybatisflex.test.entity
  # Mapper 接口扫描路径(也可通过 @MapperScan 注解配置)
  mapper-locations: classpath:mappers/**/*.xml
  # 开启 SQL 日志打印(开发环境推荐开启)
  log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2.5 配置启动类

在 Spring Boot 启动类上添加 @MapperScan 注解,扫描 Mapper 接口所在包:

java 复制代码
package com.mybatisflex.test;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.mybatisflex.test.mapper") // Mapper 接口路径
public class MybatisFlexTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisFlexTestApplication.class, args);
    }
}

三、基础用法实战

3.1 编写实体类(Entity)

使用 MyBatis-Flex 注解映射数据库表与字段,支持主键策略、逻辑删除、字段填充等配置:

java 复制代码
package com.mybatisflex.test.entity;

import com.mybatisflex.annotation.*;
import lombok.Data;
import java.util.Date;

@Data // Lombok 注解,自动生成 getter/setter
@Table("tb_account") // 映射数据库表名
@TableLogic(value = "0", deletedValue = "1") // 逻辑删除:未删0,已删1
public class Account {
    // 主键,自增策略
    @Id(keyType = KeyType.Auto)
    private Long id;

    // 字段名与数据库一致,可省略 @Column
    private String userName;

    private Integer age;

    private Date birthday;

    // 逻辑删除字段,与 @TableLogic 配合使用
    private Integer isDeleted;

    // 自动填充创建时间(插入时触发)
    @Column(fill = FillInsert.class)
    private Date createTime;
}

核心注解说明:

  • @Table:指定实体类对应数据库表名,支持逻辑删除全局配置。

  • @Id:标识主键,keyType 支持自增(Auto)、UUID、雪花算法等。

  • @Column:配置字段属性,fill 支持插入/更新时自动填充。

3.2 编写 Mapper 接口

Mapper 接口继承 MyBatis-Flex 提供的 BaseMapper,即可获得全套单表 CRUD 方法,无需编写 XML 或注解:

java 复制代码
package com.mybatisflex.test.mapper;

import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.test.entity.Account;
import org.springframework.stereotype.Repository;

// @Repository 注解用于 Spring 扫描管理
@Repository
public interface AccountMapper extends BaseMapper<Account> {
    // 无需额外编写方法,BaseMapper 已提供全套单表操作
}

BaseMapper核心方法包括:insert(新增)、update(更新)、deleteById(按ID删除)、selectById(按ID查询)、selectList(查询列表)等。

3.3 测试单表 CRUD 操作

编写单元测试,验证基础 CRUD 功能,注入 AccountMapper 即可调用方法:

java 复制代码
package com.mybatisflex.test;

import com.mybatisflex.test.entity.Account;
import com.mybatisflex.test.mapper.AccountMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
import java.util.List;

@SpringBootTest
public class AccountMapperTest {
    @Autowired
    private AccountMapper accountMapper;

    // 新增测试
    @Test
    public void testInsert() {
        Account account = new Account();
        account.setUserName("赵六");
        account.setAge(25);
        account.setBirthday(new Date());
        // 调用 BaseMapper 自带方法
        int rows = accountMapper.insert(account);
        System.out.println("新增行数:" + rows + ",新增ID:" + account.getId());
    }

    // 按ID查询测试
    @Test
    public void testSelectById() {
        Account account = accountMapper.selectById(1L);
        System.out.println("查询结果:" + account);
    }

    // 条件查询测试
    @Test
    public void testSelectList() {
        // 使用 QueryWrapper 构建查询条件
        QueryWrapper queryWrapper = QueryWrapper.create()
                .where(Account::getAge).gt(18) // 年龄大于18
                .and(Account::getUserName).like("张"); // 用户名包含"张"
        List<Account> accounts = accountMapper.selectListByQuery(queryWrapper);
        System.out.println("条件查询结果:" + accounts);
    }

    // 更新测试
    @Test
    public void testUpdate() {
        Account account = new Account();
        account.setId(1L);
        account.setAge(19); // 修改年龄
        int rows = accountMapper.update(account);
        System.out.println("更新行数:" + rows);
    }

    // 逻辑删除测试(实际执行 UPDATE,而非 DELETE)
    @Test
    public void testDeleteById() {
        int rows = accountMapper.deleteById(2L);
        System.out.println("删除行数:" + rows);
    }
}

四、核心特性详解

4.1 灵活的 QueryWrapper 条件构造器

MyBatis-Flex 提供的 QueryWrapper 支持链式调用,可构建复杂查询条件,包括多表关联、排序、分页等,且语法简洁易懂:

java 复制代码
// 1. 多条件组合查询
QueryWrapper queryWrapper = QueryWrapper.create()
        .select(Account::getId, Account::getUserName, Account::getAge) // 只查询指定字段
        .from(Account.class)
        .where(Account::getAge).between(18, 30) // 年龄在18-30之间
        .and(Account::getUserName).notLike("李") // 排除用户名含"李"的记录
        .orderBy(Account::getCreateTime).desc(); // 按创建时间倒序

// 2. 分页查询(第1页,每页10条)
Page<Account> page = accountMapper.paginate(1, 10, queryWrapper);
System.out.println("总条数:" + page.getTotalRows());
System.out.println("分页数据:" + page.getRecords());

// 3. 多表关联查询(示例:关联订单表,查询用户及对应订单数)
QueryWrapper joinQuery = QueryWrapper.create()
        .select(Account::getId, Account::getUserName)
        .select("COUNT(order.id) as order_count")
        .from(Account.class)
        .leftJoin(Order.class).on(Account::getId, Order::getAccountId)
        .groupBy(Account::getId)
        .having("order_count > 0");

4.2 批量操作优化

MyBatis-Flex 提供高效批量插入/更新方法,底层优化 SQL 执行效率,避免循环单条操作的性能问题:

java 复制代码
// 批量插入
@Test
public void testInsertBatch() {
    List<Account/> accountList = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        Account account = new Account();
        account.setUserName("批量用户" + i);
        account.setAge(20 + i);
        account.setBirthday(new Date());
        accountList.add(account);
    }
    // 批量插入,返回成功行数
    int rows = accountMapper.insertBatch(accountList);
    System.out.println("批量插入行数:" + rows);
}

// 批量更新(按ID更新)
@Test
public void testUpdateBatch() {
    List<Account> accountList = accountMapper.selectListByQuery(QueryWrapper.create().where(Account::getAge).lt(20));
    accountList.forEach(account -> account.setAge(20)); // 统一将年龄改为20
    int rows = accountMapper.updateBatch(accountList);
    System.out.println("批量更新行数:" + rows);
}

4.3 数据脱敏与字段权限控制

4.3.1 数据脱敏

通过 @DataMask 注解实现字段脱敏,支持姓名、手机号、邮箱等常见场景,查询时自动脱敏,不影响数据库存储:

java 复制代码
import com.mybatisflex.annotation.DataMask;
import com.mybatisflex.annotation.DataMaskType;

public class Account {
    // 其他字段省略...
    
    // 姓名脱敏:保留姓氏,名字替换为*(如"张三"→"张*")
    @DataMask(type = DataMaskType.NAME)
    private String userName;

    // 手机号脱敏:保留前3位和后4位(如"13800138000"→"138****8000")
    @DataMask(type = DataMaskType.MOBILE)
    private String phone;
}

4.3.2 字段权限控制

通过 @Table 注解的 fieldPermission 属性,基于角色控制字段访问权限,适用于多角色场景:

java 复制代码
@Table(value = "tb_account", fieldPermission = {
        @FieldPermission(role = "admin", columns = {"*"}), // 管理员可查看所有字段
        @FieldPermission(role = "user", columns = {"id", "userName", "age"}) // 普通用户仅查看部分字段
})
public class Account {
    // 字段定义省略...
}

使用时需设置当前角色,查询时自动过滤无权限字段:

java 复制代码
// 设置当前角色为普通用户
QueryWrapper queryWrapper = QueryWrapper.create()
        .setRole("user")
        .where(Account::getAge).gt(18);
List<Account> accounts = accountMapper.selectListByQuery(queryWrapper);
// 返回结果仅包含 id、userName、age 字段

4.4 多租户支持

MyBatis-Flex 原生支持多租户,通过配置 TenantHandler 自动为查询、新增、更新操作添加租户条件,无需手动拼接:

java 复制代码
// 1. 实现 TenantHandler 接口
public class MyTenantHandler implements TenantHandler {
    // 租户ID字段名(数据库表中需包含该字段,如 tenant_id)
    @Override
    public String getTenantIdColumn() {
        return "tenant_id";
    }

    // 获取当前租户ID(实际场景从上下文获取,如登录用户信息)
    @Override
    public Object getTenantId() {
        return SecurityUtils.getCurrentTenantId(); // 自定义方法,获取当前租户ID
    }
}

// 2. 配置多租户(全局生效)
@Configuration
public class MyBatisFlexConfig {
    @Bean
    public MyBatisFlexCustomizer myBatisFlexCustomizer() {
        return config -> {
            config.setTenantHandler(new MyTenantHandler());
        };
    }
}

配置后,所有操作自动添加 tenant_id = 当前租户ID 条件,无需修改现有代码,实现租户数据隔离。

五、高级配置与最佳实践

5.1 自定义 SQL 与 XML 映射

MyBatis-Flex 支持原生 MyBatis XML 映射,适合复杂 SQL 场景,可与增强功能混用。例如,在 AccountMapper.xml 中编写自定义 SQL:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatisflex.test.mapper.AccountMapper"/>
    <!-- 自定义查询:查询指定年龄段的用户数量 -->
    <select id="countByAgeRange" resultType="java.lang.Integer">
        SELECT COUNT(*) FROM tb_account
        WHERE age BETWEEN #{minAge} AND #{maxAge}
        AND is_deleted = 0
    </select>
</mapper>

在 Mapper 接口中添加对应方法:

java 复制代码
public interface AccountMapper extends BaseMapper<Account> {
    // 自定义方法,与 XML 中 id 对应
    Integer countByAgeRange(@Param("minAge") Integer minAge, @Param("maxAge") Integer maxAge);
}

5.2 性能优化建议

  • 批量操作优先使用 insertBatch/updateBatch,替代循环单条操作,减少数据库连接次数。

  • 复杂查询优先使用 XML 自定义 SQL,简单查询用QueryWrapper,平衡开发效率与性能。

  • 启用 MyBatis 二级缓存(针对静态数据),减少重复查询:在 Mapper 接口添加 @CacheNamespace 注解。

  • 忽略无需持久化的字段,通过 @Column(ignore = true) 标注,避免多余字段参与 SQL 构建。

5.3 常见问题排查

  • 字段映射失败 :检查实体类注解 @Table@Column 是否正确,数据库字段名与实体类字段名是否一致(可开启 SQL 日志排查生成的 SQL)。

  • 逻辑删除不生效 :确保实体类添加@TableLogic 注解,且逻辑删除字段类型与配置值一致(如 TINYINT 对应 0/1)。

  • 多租户条件未添加 :检查 TenantHandler 实现是否正确,当前租户 ID 是否能正常获取,确保配置类被 Spring 扫描。

六、总结

MyBatis-Flex 作为一款轻量级 MyBatis 增强框架,既保留了原生 MyBatis 的灵活性与高性能,又通过简洁的 API、丰富的企业级特性,大幅降低了日常开发工作量。其无第三方依赖、无性能损耗的设计,使其在中小型项目快速开发与大型项目复杂场景中均能适用。

对于从 MyBatis 迁移的项目,MyBatis-Flex 可平滑集成,无需修改现有 XML 与 SQL;对于新项目,其 QueryWrapper、批量操作、多租户等特性,能有效提升开发效率,同时保持对 SQL 的绝对控制。建议在实际开发中根据业务场景灵活运用其增强功能,平衡开发效率与系统性能。

更多细节可参考官方文档:https://mybatis-flex.com/

相关推荐
没有bug.的程序员2 小时前
Spring Boot 与 Sleuth:分布式链路追踪的集成、原理与线上故障排查实战
java·spring boot·分布式·后端·分布式链路追踪·sleuth·线上故障排查
专注VB编程开发20年2 小时前
无 $ 后缀的变体版函数(Mid、InStr)
java·开发语言
牛马1112 小时前
flutter Riverpod 中的 overrideWith
android·java·flutter
弓弧名家_玄真君2 小时前
在ubuntu中安装redis
前端·bootstrap·mybatis
熊猫钓鱼>_>2 小时前
深入理解Java堆栈:从原理到面试实战
java·开发语言·面试·职场和发展·面向对象·堆栈·oop
cici158742 小时前
基于MATLAB的非正交多址(NOMA)系统协同中继技术提升小区边缘用户性能实现
java·服务器·matlab
bigdata-rookie2 小时前
Starrocks 数据模型
java·前端·javascript
爱敲代码的憨仔2 小时前
Spring-AOP
java·后端·spring
风景的人生2 小时前
request请求的@RequestParm标注的参数也需要放在请求路径后
java