Mybatis-Flex的简单入门,Mybatis-Flex和其它框架对比

发布于:2024-12-07 ⋅ 阅读:(163) ⋅ 点赞:(0)

前言

        最近做项目,项目组必须要用Mybatis-Flex,然后自己去学了一下,给大家进行分享。

        MyBatis-Flex 是 MyBatis 的一个增强工具,旨在简化 MyBatis 的使用并提高开发效率。它通过提供一系列的便捷方法和特性来减少重复代码的编写,使得数据库操作更加直观和高效。MyBatis-Flex 专注于提升开发者体验,尤其是在处理CRUD(创建、读取、更新、删除)操作时。

Mybatis-Flex的官网:MyBatis-Flex - MyBatis-Flex 官方网站

📌 特征

  1. 轻量:除了 MyBatis,没有任何第三方依赖、没有任何拦截器,在执行的过程中,没有任何的 Sql 解析(Parse)。 这带来了几个好处:极高的性能、极易对代码进行跟踪和调试、把控性更高。

  2. 灵活:支持 Entity 的增删改查、以及分页查询的同时,MyBatis-Flex 提供了 Db + Row 工具,可以无需实体类对数据库进行增删改查以及分页查询。 与此同时,MyBatis-Flex 内置的 QueryWrapper 可以轻易的帮助我们实现 多表查询、链接查询、子查询 等等常见的 SQL 场景。

  3. 强大:支持任意关系型数据库,还可以通过方言持续扩展,同时支持 多(复合)主键、逻辑删除、乐观锁配置、数据脱敏、数据审计、 数据填充 等等功能简单来说

简单来说,Mybatis-Flex 相比 Mybatis-Plus 等框架 速度更快、功能更多、代码更简洁~ 

 1、准备

1.1 首先准备一个数据库,我这里准备的是course表格,课程表

1.2 Spring Boot 项目初始化

此时需要创建 Spring Boot 项目,并添加 Maven 依赖;此处我通过 IDEA 使用 Spring Initializer 快速初始化一个 Spring Boot 工程。

📌 创建新项目,以下是相关参数:

📌 指定 spring boot 版本和勾选 web 依赖

1.3 添加 Maven 主要依赖

往 pom.xml 文件中添加以下依赖,由于本项目是通过 Spring Initializer 搭建,因此已存在 spring-boot-starter-web 和 spring-boot-starter-test 依赖。

      <dependency>
            <groupId>com.mybatis-flex</groupId>
            <artifactId>mybatis-flex-spring-boot-starter</artifactId>
            <version>1.5.4</version>
        </dependency>

1.4 配置数据源

在 application.properties 或 application.yml 中配置数据源:

#数据库连接配置
spring.datasource.username=root
spring.datasource.password=root
#mysql5~8 驱动不同driver-class-name     8需要增加时区的配置serverTimezone=UTC,放在url最后
#useSSL=false 安全连接
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

3 Mybatis-Flex 实践
3.1 编写实体类和 Mapper 接口
路径:src/main/java/com/yinyu/flex/pojo/User.java

📌 User 实体类

使用 @Table("sf_sourse") 设置实体类与表名的映射关系
使用 @Id(keyType = KeyType.Auto) 标识主键为自增

大家进行测试的话只需要加我画框框的地方就可以了 

 

 Mapper 接口继承 BaseMapper 接口

路径:src/main/java/com/yinyu/flex/mapper/UserMapper.java

这部分也可以使用 MyBatis-Flex 的代码生成器来生,功能非常强大的。详情进入:代码生成器章节 了解。

3.2 在主启动类添加 @MapperScan 注解

路径:src/main/java/com/yinyu/flex/MybatisFlexApplication.java

用于扫描 Mapper 文件夹:

package com.yinyu.flex;
 
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
@MapperScan("com.yinyu.flex.mapper")
public class MybatisFlexApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(MybatisFlexApplication.class, args);
    }
 
}

就可以进行测试了

    /**
     * 查询课程列表
     *
     * @param sfCourse 课程
     * @return 课程
     */
    @Override
    public List<SfCourse> selectSfCourseList(SfCourse sfCourse,Integer  pageNum, Integer pageSize) {
        // 创建 QueryWrapper 实例
        QueryWrapper queryWrapper = query()
                .and(SF_COURSE.COURSE_ID.eq(sfCourse.getCourseId()))
                .and(SF_COURSE.COURSE_CODE.eq(sfCourse.getCourseCode()))
                .and(SF_COURSE.SEMESTER_ID.eq(sfCourse.getSemesterId()))
                .and(SF_COURSE.COURSE_NAME.eq(sfCourse.getCourseName()))
                .and(SF_COURSE.SUBJECT_ID.eq(sfCourse.getSubjectId()));
        if (pageNum != null && pageSize != null)
            return sfCourseMapper.paginate(pageNum, pageSize, queryWrapper).getRecords();
        else
            return sfCourseMapper.selectListByQuery(queryWrapper);
    }

上面是我的测试案例。

2、MyBatis-FlexMyBatis-Plus:的对比 

基础查询对比

MyBatis-Flex:

QueryWrapper query = QueryWrapper.create()
        .where(EMPLOYEE.LAST_NAME.like(searchWord)) //条件为null时自动忽略
        .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);

或者 MyBatis-Plus 的 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);

Fluent-MyBatis:

EmployeeQuery query = new EmployeeQuery()
    .where.lastName().like(searchWord, If::notNull)
    .and.gender().eq(1)
    .and.age().gt(24)
    .end();
List<Employee> employees = employeeMapper.listEntity(query);

查询集合函数

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);

缺点:字段硬编码,容易拼错。无法使用 ide 的字段进行重构,无法使用 IDE 自动提示,发生错误不能及时发现。

Fluent-MyBatis:

EmployeeQuery query = new EmployeeQuery()
        .select
        .id()
        .userName()
        .max.birthday()
        .avg.sex("sex_avg")
        .end()
List<Employee> employees = employeeMapper.listEntity(query);

缺点:编写内容不符合 sql 直觉。

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"));

Fluent-MyBatis:

AccountQuery query = new AccountQuery()
    .where.id().ge(100)
    .and(
            new AccountQuery().where.sex().eq(1).or(
                new AccountQuery().where.sex().eq(2).end()
    ).end())
    .or(
        new AccountQuery().where.age.in(18,19,20)
            .and.userName().like("michael").end()
    )
    .end();

缺点:许多 .end() 方法调用,容易忘记出错(或者写错了?欢迎纠正)。

多表查询 1

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);

MyBatis-Plus:

// 不支持~~~~

Fluent-MyBatis:

StudentQuery leftQuery = new StudentQuery("a1").selectAll()
    .where.age().eq(34)
    .end();
HomeAddressQuery rightQuery = new HomeAddressQuery("a2")
    .where.address().like("address")
    .end();

IQuery query = leftQuery
    .join(rightQuery)
    .on(l -> l.where.homeAddressId(), r -> r.where.id()).endJoin()
    .build();

List<StudentEntity> entities = this.mapper.listEntity(query);

缺点:编写内容不符合 sql 直觉。同时在编写 end() 和 endJoin() 容易忘记。

多表查询 2

假设查询的 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));

MyBatis-Plus:

// 不支持~~~~

Fluent-MyBatis:

// 不支持~~~~

PS:也有可能是笔者自己不知道如何支持,而非 Fluent-MyBatis 原因,有知道的同学可以给下示例代码。

部分字段更新

假设一个实体类 Account 中,我们要更新其内容如下:

  • userName 为 "michael"
  • age 为 "18"
  • birthday 为 null

其他字段保持数据库原有内容不变,要求执行的 SQL 如下:

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 代码如下(或可使用 MyBatis-Plus 的 LambdaUpdateWrapper,但性能没有 UpdateWrapper 好):

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);

Fluent-MyBatis 代码如下:

AccountUpdate update = new AccountUpdate()
.update.userName().is("michael")
.age().is(18)
.birthday().is(null)
.end()
.where.id().eq(100)
.end();
accountMapper.updateBy(update);

三种框架对比

功能或特点 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 的事务管理,比如 @Transactional 和 TransactionTemplate 等
多数据源是否支持 "非Spring" 项目
多租户
动态表名
动态 Schema

最后

        选择哪一个框架应该基于项目的具体需求和技术栈的选择。如果你追求极致的性能并且可以接受较小的社区支持,那么 MyBatis-Flex 可能是一个不错的选择;而如果你更看重社区支持和丰富的插件生态,那么 MyBatis-Plus 则可能更适合你。