MyBatis-Flex学习手册

MyBatis-Flex简介

MyBatis-Flex是什么

MyBatis-Flex是一个优雅的MyBatis增强框架,它非常轻量、同时拥有极高的性能与灵活性。我们可以使用MyBatis-Flex链接任何数据库,其内置的QueryWrapper帮助我们极大的减少了SQL编写的工作的同时,减少出错的可能性。


特征

1、轻量

除了MyBatis,没有任何第三方依赖、没有任何拦截器,其原理是通过SqlProvider的方式实现的。同时,在执行的过程中,没有任何的Sql解析(Parse)。这带来的几个好处:高性能,代码易于跟踪调试,可把控性高。

2、灵活

支持Entity的增删改查、以及分页查询的同时,MyBatis-Flex提供了Db + Row工具,可以无需实体类对数据库进行增删改查以及分页查询。与此同时,MyBatis-Flex 内置的QueryWrapper可以轻易的实现多表查询、连接查询、子查询等。

3、强大

支持任意关系型数据库,还可以通过方言持续扩展,同时支持复合主键、逻辑删除、乐观锁配置、数据脱敏、数据审计、数据填充等功能。

快速开始

示例采用IDEA作为开发工具,maven构建工程,使用Java语言开发,数据库使用MySQL,选择2.7.15版本的Spring Boot框架(低版本会出现TransactionManager接口不存在的情况),选择的mysql驱动包是mysql-connector-j(避免出现HikariDataSource异常问题)。

1、创建maven工程flex,引入相关的依赖


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>

    <groupId>org.qcx.mybatisgroupId>
    <artifactId>flexartifactId>
    <version>1.0-SNAPSHOTversion>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <maven.compiler.source>1.8maven.compiler.source>
        <maven.compiler.target>1.8maven.compiler.target>
    properties>

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.7.15version>
        <relativePath/>
    parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>com.mybatis-flexgroupId>
            <artifactId>mybatis-flex-spring-boot-starterartifactId>
            <version>1.6.5version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>
        <dependency>
            <groupId>com.mysqlgroupId>
            <artifactId>mysql-connector-jartifactId>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>com.zaxxergroupId>
            <artifactId>HikariCPartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

project>

2、定义表结构并初始化数据

create table if not exists `tb_account`
(
  `id` integer primary key auto_increment,
  `user_name` varchar(100),
  `age` integer,
  `birthday` datetime
);

insert into tb_account(id, user_name, age, birthday)
values (1, '张三', 20, '2023-01-11'),
       (2, '李四', 22, '2023-09-16');

3、对Spring Boot项目进行配置

(1)新建配置文件application.yml,添加数据源配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: root

(2)新建启动类Application.java

package org.qcx.mybatis.flex;

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

@SpringBootApplication
//扫描Mapper文件夹
@MapperScan(basePackages = "org.qcx.mybatis.flex.mapper")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

4、编写实体类Account.java和Mapper接口AccountMapper.java

package org.qcx.mybatis.flex.entity;

import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.Data;
import java.util.Date;

@Data
@Table("tb_account")  //设置实体类与表名的映射关系
public class Account {

    @Id(keyType = KeyType.Auto)  //标识主键为自增
    private Long id;

    private String userName;

    private Integer age;

    private Date birthday;
}

package org.qcx.mybatis.flex.mapper;

import com.mybatisflex.core.BaseMapper;
import org.qcx.mybatis.flex.entity.Account;

public interface AccountMapper extends BaseMapper<Account> {

}

5、添加测试类ApplicationTest.java进行功能测试,MyBatis-Flex使用了APT(Annotation Processing Tool)技术,在项目编译的时候,会自动根据Entity类定义的字段生成“ACCOUNT”类以及Entity对应的Mapper类,通过开发工具构建工具或者执行mvn clean package都可以自动生成。

package org.qcx.mybatis.flex;

import com.mybatisflex.core.query.QueryWrapper;
import org.junit.jupiter.api.Test;
import org.qcx.mybatis.flex.entity.Account;
import org.qcx.mybatis.flex.mapper.AccountMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.qcx.mybatis.flex.entity.table.AccountTableDef.ACCOUNT;

@SpringBootTest
public class ApplicationTest {

    @Autowired
    private AccountMapper accountMapper;

    @Test //Test注解的包不是org.junit
    public void contextLoads() {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .select()
                .where(ACCOUNT.AGE.eq("20"));
        Account account = accountMapper.selectOneByQuery(queryWrapper);
        System.out.println(account);
    }

}

Account(id=1, userName=张三, age=20, birthday=Wed Jan 11 00:00:00 CST 2023)

MyBatis-Flex和同类框架对比

MyBatis-Flex主要是和MyBatis-PlusFluent-MyBatis对比。MyBatis-Plus是老牌的MyBatis增强框架,Fluent-MyBatis是阿里云开发的MyBatis增强框架。

功能对比

功能或特点 MyBatis-Flex MyBatis-Plus Fluent-MyBatis
对 entity 的基本增删改查
分页查询
分页查询之总量缓存
分页查询无 SQL 解析设计(更轻量,及更高性能)
多表查询: from 多张表
多表查询: left join、inner join 等等
多表查询: union,union all
单主键配置
多种 id 生成策略
支持多主键、复合主键
字段的 typeHandler 配置
除了 MyBatis,无其他第三方依赖(更轻量)
QueryWrapper 是否支持在微服务项目下进行 RPC 传输 未知
逻辑删除
乐观锁
SQL 审计
数据填充
数据脱敏 ✔️ (收费)
字段权限 ✔️ (收费)
字段加密 ✔️ (收费)
字典回写 ✔️ (收费)
Db + Row
Entity 监听
多数据源支持 借助其他框架或收费
多数据源是否支持 Spring 的事务管理,比如 @TransactionalTransactionTemplate
多数据源是否支持 “非Spring” 项目
多租户
动态表名
动态 Schema

基础查询

MyBatis-Flex:

QueryWrapper query = QueryWrapper.create()
    //条件为空时自动忽略
    .where(EMPLOYEE.LAST_NAME.like(searchWord))
    .and(EMPLOYEE.GENDER.eq(1))
    .and(EMPLOYEE.AGE.gt(24));
List<Employee> employees = employeeMapper.selectListByQuery(query);	

MyBatis-Plus:

QueryWrapper<Employee> queryWrapper = Wrappers.query()
        .like(searchWord != null, "last_name", searchWord)
        .eq("gender", 1)
        .gt("age", 24);
List<Employee> employees = employeeMapper.selectList(queryWrapper);

或者Lambda写法:

LambdaQueryWrapper<Employee> queryWrapper = Wrappers.<Employee>lambdaQuery()
        .like(StringUtils.isNotEmpty(searchWord), Employee::getUserName,"B")
        .eq(Employee::getGender, 1)
        .gt(Employee::getAge, 24);
List<Employee> employees = employeeMapper.selectList(queryWrapper);

查询集合函数

Mybatis-Flex:

QueryWrapper query = QueryWrapper.create()
    .select(
        ACCOUNT.ID,
        ACCOUNT.USER_NAME,
        max(ACCOUNT.BIRTHDAY),
        avg(ACCOUNT.SEX).as("sex_avg")
    );
List<Employee> employees = employeeMapper.selectListByQuery(query);

MyBatis-Plus:

QueryWrapper<Employee> queryWrapper = Wrappers.query()
    .select(
        "id",
        "user_name",
        "max(birthday)",
        "avg(birthday) as sex_avg"
    );
List<Employee> employees = employeeMapper.selectList(queryWrapper);

and(…)和or(…)

假设我们需要构建SQL查询,在SQL中添加括号。

SELECT * FROM tb_account
WHERE id >= 100
AND (sex = 1 OR sex = 2)
OR (age IN (18,19,20) AND user_name LIKE "%michael%" )

MyBatis-Flex:

QueryWrapper query = QueryWrapper.create()
    .where(ACCOUNT.ID.ge(100))
    .and(ACCOUNT.SEX.eq(1).or(ACCOUNT.SEX.eq(2)))
    .or(ACCOUNT.AGE.in(18, 19, 20).and(ACCOUNT.USER_NAME.like("michael")));

MyBatis-Plus:

QueryWrapper<Employee> query = Wrappers.query()
        .ge("id", 100)
        .and(i -> i.eq("sex", 1).or(x -> x.eq("sex", 2)))
        .or(i -> i.in("age", 18, 19, 20).like("user_name", "michael"));
// or lambda
LambdaQueryWrapper<Employee> query = Wrappers.<Employee>lambdaQuery()
        .ge(Employee::getId, 100)
        .and(i -> i.eq(Employee::getSex, 1).or(x -> x.eq(Employee::getSex, 2)))
        .or(i -> i.in(Employee::getAge, 18, 19, 20).like(Employee::getUserName, "michael"));

多表查询

MyBatis-Flex:

QueryWrapper query = QueryWrapper.create()
    .select().from(ACCOUNT)
    .leftJoin(ARTICLE).on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
    .where(ACCOUNT.AGE.ge(10));

List<Account> accounts = mapper.selectListByQuery(query);

假设查询如下SQL:

SELECT a.id, a.user_name, b.id AS articleId, b.title
FROM tb_account AS a, tb_article AS b
WHERE a.id = b.account_id

MyBatis-Flex:

QueryWrapper query = new QueryWrapper()
.select(
      ACCOUNT.ID
    , ACCOUNT.USER_NAME
    , ARTICLE.ID.as("articleId")
    , ARTICLE.TITLE)
.from(ACCOUNT.as("a"), ARTICLE.as("b"))
.where(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID));

部分字段更新

假设一个实体类Account中,需要更新userName为Tom,age为20,birthday为null。

update tb_account
set user_name = "michael", age = 18, birthday = null
where id = 100

MyBatis-Flex:

Account account = UpdateEntity.of(Account.class);
account.setId(100); //设置主键
account.setUserName("michael");
account.setAge(18);
account.setBirthday(null);
accountMapper.update(account);

MyBatis-Plus:

UpdateWrapper<Account> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", 100);
updateWrapper.set("user_name", "michael");
updateWrapper.set("age", 18);
updateWrapper.set("birthday", null);
accountMapper.update(null, updateWrapper);

分页查询

MyBatis-Flex:

QueryWrapper queryWrapper = new QueryWrapper()
    .where(FLEX_ACCOUNT.ID.ge(100));
mapper.paginate(page, pageSize, 20000, queryWrapper);

MyBatis-Plus:

LambdaQueryWrapper<PlusAccount> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.ge(PlusAccount::getId, 100);
    queryWrapper.eq(PlusAccount::getEmail, "[email protected]");
Page<PlusAccount> p = Page.of(page, pageSize, 20000, false);
mapper.selectPage(p, queryWrapper);

使用MyBatis原生功能

使用MyBatis-Flex增强框架不会影响原有的MyBatis的任何功能。

使用@select等原生注解

MyBatis提供了@Insert@Delete@Update@Select注解,用于对Mapper的方法进行配置,用于原生编写原生 SQL 进行增删改查。

package org.qcx.mybatis.flex.mapper;

import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.qcx.mybatis.flex.entity.Account;

public interface AccountMapper extends BaseMapper<Account> {

    @Select("select * from tb_account where id=#{id}")
    Account selectById(@Param("id") Object id);
}

使用xml方式

使用xml方法需要在application.yml中配置xml加载路径。

mybatis-flex:
  mapper-locations:
    - classpath*:/mapper/*.xml

mapper:

public interface AccountMapper extends BaseMapper<Account> {
    Account selectByName(@Param("name") String name);
}

xml:


DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.qcx.mybatis.flex.mapper.AccountMapper">

    <select id="selectByName" resultType="org.qcx.mybatis.flex.entity.Account">
        select * from `tb_account` where `user_name` = #{name}
    select>

mapper>

XML分页

XML分页在MyBatis-Flex的v1.5.5版本开始提供的XML分页解决方案,方便使用XML对数据进行分页。

QueryWrapper qw = QueryWrapper.create()
    .where(Account::getAge).eq(18)
    .and(Account::getId).ge(0);
//selectByName是