mybatis-flex/docs/zh/intro/comparison.md
2023-07-28 13:54:14 +08:00

313 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MyBatis-Flex 和同类框架「功能」对比
MyBatis-Flex 主要是和 `MyBatis-Plus``Fluent-MyBatis` 对比内容来源其官网、git 或者 网络文章,若有错误欢迎纠正。
- MyBatis-Plus老牌的 MyBatis 增强框架,开源于 2016 年。
- Fluent-MyBatis阿里云开发的 MyBatis 增强框架(来自于阿里云·云效产品团队)
> 本文只阐述了「功能」方面的对比,**「性能」** 对比请参考 [这里](./benchmark.md)。
> 若发现对比中有错误,请加入 MyBatis-Flex QQ 交流群532992631然后联系群主纠正。
## 功能对比
| 功能或特点 | MyBatis-Flex | MyBatis-Plus | Fluent-MyBatis |
| -------- | -------- | -------- | -------- |
| 对 entity 的基本增删改查 | ✅ | ✅ | ✅ |
| 分页查询 | ✅ | ✅ | ✅ |
| 分页查询之总量缓存 | ✅ | ✅ | ❌ |
| 分页查询无 SQL 解析设计(更轻量,及更高性能) | ✅ | ❌ | ✅ |
| 多表查询: from 多张表 | ✅ | ❌ | ❌ |
| 多表查询: left join、inner join 等等 | ✅ | ❌ | ✅ |
| 多表查询: unionunion all | ✅ | ❌ | ✅ |
| 单主键配置 | ✅ | ✅ | ✅ |
| 多种 id 生成策略 | ✅ | ✅ | ✅ |
| 支持多主键、复合主键 | ✅ | ❌ | ❌ |
| 字段的 typeHandler 配置 | ✅ | ✅ | ✅ |
| 除了 MyBatis无其他第三方依赖更轻量 | ✅ | ❌ | ❌ |
| QueryWrapper 是否支持在微服务项目下进行 RPC 传输 | ✅ | ❌ | 未知 |
| 逻辑删除 | ✅ | ✅ | ✅ |
| 乐观锁 | ✅ | ✅ | ✅ |
| SQL 审计 | ✅ | ❌ | ❌ |
| 数据填充 | ✅ | ✅ | ✅ |
| 数据脱敏 | ✅ | ✔️ **(收费)** | ❌ |
| 字段权限 | ✅ | ✔️ **(收费)** | ❌ |
| 字段加密 | ✅ | ✔️ **(收费)** | ❌ |
| 字典回写 | ✅ | ✔️ **(收费)** | ❌ |
| Db + Row | ✅ | ❌ | ❌ |
| Entity 监听 | ✅ | ❌ | ❌ |
| 多数据源支持 | ✅ | 借助其他框架或收费 | ❌ |
| 多数据源是否支持 Spring 的事务管理,比如 `@Transactional``TransactionTemplate` 等 | ✅ | ❌ | ❌ |
| 多数据源是否支持 "非Spring" 项目 | ✅ | ❌ | ❌ |
| 多租户 | ✅ | ✅ | ❌ |
| 动态表名 | ✅ | ✅ | ❌ |
| 动态 Schema | ✅ | ❌ | ❌ |
> 以上内容来自第三方相关产品的官方文档或第三方平台,若有错误,欢迎纠正。
## 基础查询
**MyBatis-Flex**
````java
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**
````java
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 写法:
```java
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**
````java
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**
````java
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**
````java
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**
````java
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 中添加括号)。
```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**
```java
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**
```java
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**
```java
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**
````java
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**
````java
// 不支持~~~~
````
**Fluent-MyBatis**
````java
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 如下:
```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**
````java
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**
````java
// 不支持~~~~
````
**Fluent-MyBatis**
````java
// 不支持~~~~
````
>PS也有可能是笔者自己不知道如何支持而非 Fluent-MyBatis 原因,有知道的同学可以给下示例代码。
## 部分字段更新
假设一个实体类 Account 中,我们要更新其内容如下:
- `userName` 为 "michael"
- `age` 为 "18"
- `birthday` 为 null
其他字段保持数据库原有内容不变,要求执行的 SQL 如下:
```sql
update tb_account
set user_name = "michael", age = 18, birthday = null
where id = 100
```
**MyBatis-Flex** 代码如下:
```java
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` 好):
```java
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** 代码如下:
```java
AccountUpdate update = new AccountUpdate()
.update.userName().is("michael")
.age().is(18)
.birthday().is(null)
.end()
.where.id().eq(100)
.end();
accountMapper.updateBy(update);
```