MyBatis Flex 讲解使用

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=UTC
    username: xxxx
    password: xxxx
    driver-class-name: com.mysql.cj.jdbc.Driver

# MyBatis-Flex 高级配置
mybatis-flex:
  mapper-locations: classpath:mapper/**/*.xml

  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 开启驼峰命名转换
    map-underscore-to-camel-case: true
  # 全局配置
  global-config:
    print-banner: true
    # 逻辑删除配置
    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 并启用 Lombok
        globalConfig.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;
}

@ColumnMaskMyBatis-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 注入的安全处理
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容