既生瑜何生亮,浅析下层出不穷的新ORM框架 MyBatis-Flex,可看可不看!!!

既生瑜何生亮,浅析下层出不穷的新ORM框架: MyBatis-Flex,可看可不看!!!

1.概述

这里先说说我的观点哈,仅是个人观点哦,不喜勿喷。现在这些框架层出不穷,其实吧个人感觉没必要过度关注,因为这些框架并没有完完全全做到推陈出新,反倒是有一点互相“学习copy”的感觉,并没有那么新颖强大、从无到有的一个过程。那说回今天的主题ORM框架,在Java后端技术栈里面我们都知道MyBatis是主流的ORM框架,现在很多公司都在使用着,后来在MyBatis基础上出现了两个比较主流的增强框架Mybatis-PlusFluent-MyBatis,这两个框架之前我们也分析总结过,下面给出文章传送门:

MyBatis-Plus: MyBatis-plus最详细的入门使用教程

一文带你掌握MyBatis-Plus的plus高级功能点如何使用,plus就该有plus的玩法

咋说呢,MyBatis-Plus开源的比较早,2016年就开源了所以用的公司挺多的,可以说是这些年MyBatis增强框架的主流首选,不太了解该框架的小伙伴可以看看上面的传送门文章总结,都是公司项目实战的心得笔记。官网地址:baomidou.com/

Fluent-MyBatis: FluentMybatis: 基于mybatis但青出于蓝

阿里云开发的 MyBatis 增强框架(来自于阿里云·云效产品团队),只是感觉使用的人没那么多,感觉没那么有名气。

tk-mybatis: tkmybatis也是对Mybatis的增强和封装,在Mybatis基础上做了二次封装的增强框架,只是这些年最近淡出,现在活跃度几乎没有, 这里就不对它进行过多解读了。

MyBatis-Flex: 今天我们浅析的主人公,它一个优雅的 MyBatis 增强框架,当下比较新,有点热度,github上有1k多的star,当然现在star的数量已经不一定是真实的啦,不再是权威指标考量了,官网地址:mybatis-flex.com/

img

红色框里面是官网给出该框架的特征所在,优势所在,厉害所在~~~

2. MyBatis-Flex 和同类框架对比

2.1 功能对比

既生瑜何生亮??? MyBatis-Flex 主要是和 MyBatis-PlusFluent-MyBatis 对比

  • MyBatis-Plus:老牌的 MyBatis 增强框架,开源于 2016 年。
  • 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 ? ? ?

以上内容来自第三方相关产品的官方文档或第三方平台,若有错误,欢迎纠正。

2.2 使用案例对比

2.2.1 基础查询

MyBatis-Flex:

ini复制代码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:

perl复制代码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 写法:

scss复制代码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:

scss复制代码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);
2.2.2 查询集合函数

MyBatis-Flex:

ini复制代码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:

erlang复制代码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:

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

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

2.2.3 and(…) 和 or(…)

假设我们要构建如下的 SQL 进行查询(需要在 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:

perl复制代码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:

perl复制代码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:

scss复制代码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() 方法调用,容易忘记出错(或者写错了?欢迎纠正)。

2.2.4 多表查询

MyBatis-Flex:

scss复制代码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:

arduino
复制代码// 不支持~~~~

Fluent-MyBatis:

scss复制代码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.2.5 多表查询

假设查询的 SQL 如下:

css复制代码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:

csharp复制代码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:

arduino
复制代码// 不支持~~~~

Fluent-MyBatis:

arduino
复制代码// 不支持~~~~

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

2.2.6 部分字段更新

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

  • userName 为 “michael”
  • age 为 “18”
  • birthday 为 null

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

ini复制代码update tb_account
set user_name = "michael", age = 18, birthday = null
where id = 100

MyBatis-Flex 代码如下:

ini复制代码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 好):

vbscript复制代码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 代码如下:

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

项目推荐:基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba企业级系统架构底层框架封装,解决业务开发时常见的非功能性需求,防止重复造轮子,方便业务快速开发和企业技术栈框架统一管理。引入组件化的思想实现高内聚低耦合并且高度可配置化,做到可插拔。严格控制包依赖和统一版本管理,做到最少化依赖。注重代码规范和注释,非常适合个人学习和企业使用

Github地址:github.com/plasticene/…

Gitee地址:gitee.com/plasticene3…

微信公众号Shepherd进阶笔记

交流探讨qun:Shepherd_126

3.Spring Boot快速整合MyBatis-Flex框架

和整合MyBatis-Plus是一样一样的,如出一辙。套路流程如下:

第 1 步:创建数据库表

sql复制代码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, '张三', 18, '2020-01-11'),
       (2, '李四', 19, '2021-03-21');

第 2 步:创建 Spring Boot 项目,并添加 Maven 依赖

可以使用 Spring Initializer 快速初始化一个 Spring Boot 工程。

需要添加的 Maven 主要依赖示例:

xml复制代码<dependencies>
    <dependency>
        <groupId>com.mybatis-flex</groupId>
        <artifactId>mybatis-flex-spring-boot-starter</artifactId>
        <version>1.7.5</version>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
    <!-- for test only -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

第 3 步:对 Spring Boot 项目进行配置

在 application.yml 中配置数据源:

yaml复制代码# DataSource Config
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/flex_test
    username: root
    password: 123456

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:

less复制代码@SpringBootApplication
@MapperScan("com.mybatisflex.test.mapper")
public class MybatisFlexTestApplication {

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

}

第 4 步:编写实体类和 Mapper 接口

这里使用了 Lombok 来简化代码。

kotlin复制代码@Data
@Table("tb_account")
public class Account {

    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;
    private Integer age;
    private Date birthday;

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

Mapper 接口继承 BaseMapper 接口:

csharp复制代码public interface AccountMapper extends BaseMapper<Account> {

}

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

第 5 步:开始使用

添加测试类,进行功能测试:

scss复制代码import static com.mybatisflex.test.entity.table.AccountTableDef.ACCOUNT;

@SpringBootTest
class MybatisFlexTestApplicationTests {

    @Autowired
    private AccountMapper accountMapper;

    @Test
    void contextLoads() {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .select()
                .where(ACCOUNT.AGE.eq(18));
        Account account = accountMapper.selectOneByQuery(queryWrapper);
        System.out.println(account);
    }

}

控制台输出:

ini
复制代码Account(id=1, userName=张三, age=18, birthday=Sat Jan 11 00:00:00 CST 2020)

4.总结

以上全部就是今天对于MyBatis-Flex框架浅浅分析总结啦,该框架并没有多少生产环境实践经验,且还在大量开发阶段,bug率有点高,所以我觉得选择该框架作为公司项目服务的ORM框架还是需要再谨慎些。个人觉得在学有余力闲暇之时,根据个人是否感兴趣可关注下也可以不关注,因为这些orm框架都差不多,即使你的公司突然要用这个框架了,再去了解下,一周时间就门儿清了,完全没得问题的,好好看看官网文档就行了,分分钟上手使用。