Bean Searcher v3.8.0 一大波新特性来袭
上篇:最近火起的 Bean Searcher 与 MyBatis Plus 倒底有啥区别?
介绍
Bean Searcher 是一款专注高级查询的只读 ORM,天生支持联表,免 DTO/VO 转换,使一行代码实现复杂列表检索成为可能!
它可以像 基础组件 ORM 那样使用,同时也拥有 接近低代码 的开发效率。
使用场景
Bean Searcher 专治复杂的检索需求,例如:
- 普通后端查询
- 动态检索方式
- 动态分组查询
以上场景 Bean Searcher 都可以一行代码实现后端代码,极大提高生产力。
设计思想(出发点)
开发效率可以极大提升的原因
框架性能
性能不是主要卖点,但也比传统 ORM 框架平均提升 2 ~ 5 倍(Java 层性能)
代码仓库
- Github: github.com/troyzhxu/be…
- Gitee: gitee.com/troyzhxu/be…
文档与博客
新版本 v3.8.0 的变更
1、提升数据库兼容性
新增 参数转换器 机制,使 Oracle、PgSql 等数据库也同 MySQL 般顺滑。
2、参数构建器支持 自定义 SQL 条件
自定义 SQL 条件,只能在后端通过参数构建器使用,例如
Map<String, Object> params = MapUtils.builder()
// 生成 SQL 条件:u.id in (select user_id from xxx)
.field(User::getId).sql("$1 in (select user_id from xxx)")
.build();
List<User> users = searcher.searchList(User.class, params);
用 $n
来表示所需引用的第 n
个字段,再如:
Map<String, Object> params = MapUtils.builder()
// 生成 SQL 条件:id < 100 or age > 10
.field(User::getId, User::getAge).sql("$1 < 100 or $2 > 10")
.build();
List<User> users = searcher.searchList(User.class, params);
也可以在自定义的 SQL 片段中使用参数占位符 ?
(作为 JDBC 参数),例如:
Map<String, Object> params = MapUtils.builder()
// 生成 SQL 条件:id < ? or age > ?,两个占位符参数分别为:100,10
.field(User::getId, User::getAge).sql("$1 < ? or $2 > ?", 100, 10)
.build();
List<User> users = searcher.searchList(User.class, params);
3、分组动态查询条件生成机制
- 分组字段 生成
where
条件,其它字段 生成having
条件
例如一个指定了 groupBy
的 SearchBean:
@SearchBean(
tables = "student_course",
groupBy = "course_id" // 按课程 ID 分组
)
public class CourseScore {
@DbField("course_id")
private long courseId;
@DbField("sum(score)") // 该课程的总分(聚合函数:sum)
private long totalScore;
}
当以 courseId
为条件时:
Map<String, Object> params = MapUtils.builder()
.field(CourseScore::getCourseId, 101).op(LessThan.class)
.build();
List<CourseScore> list = searcher.searchList(CourseScore.class, params);
执行 SQL(条件在 where 里):
select course_id, sum(score) from student_course where course_id < 101 group by course_id
当以 totalScore
为条件时:
Map<String, Object> params = MapUtils.builder()
.field(CourseScore::getTotalScore, 500).op(GreateThan.class)
.build();
List<CourseScore> list = searcher.searchList(CourseScore.class, params);
执行 SQL(条件在 having 里):
select course_id, sum(score)
from student_course
group by course_id
having sum(score) > 500
当然两者还可以组合:
Map<String, Object> params = MapUtils.builder()
.field(CourseScore::getCourseId, 101).op(LessThan.class)
.field(CourseScore::getTotalScore, 500).op(GreateThan.class)
.build();
List<CourseScore> list = searcher.searchList(CourseScore.class, params);
执行 SQL:
select course_id, sum(score)
from student_course
where course_id < 101
group by course_id
having sum(score) > 500
4、新增 NotLike 运算符
目前内置的运算符已经有 19 个:
运算符 | 缩写 | SQL 片段 | 是否忽略空值 | 含义 |
---|---|---|---|---|
Equal | eq | x = ? | 是 | 等于(是缺省默认的运算符) |
NotEqual | ne | x != ? | 是 | 不等于 |
GreaterThan | gt | x > ? | 是 | 大于 |
GreaterEqual | ge | x >= ? | 是 | 大于等于 |
LessThan | lt | x < ? | 是 | 小于 |
LessEqual | le | x <= ? | 是 | 小于等于 |
Between | bt | x between ?1 and ?2 / x >= ?1 / x <= ?2 | 是 | 在...之间(范围查询) |
NotBetween | nb | x not between ?1 and ?2 / x < ?1 / x > ?2 | 是 | 不在...之间(范围查询)(since v3.3) |
Contain | ct | x like '%?%' | 是 | 包含(模糊查询)(since v3.2) |
StartWith | sw | x like '?%' | 是 | 以...开头(模糊查询) |
EndWith | ew | x like '%?' | 是 | 以...结尾(模糊查询) |
OrLike | ol | x like ?1 or x like ?2 or ... | 是 | 模糊或匹配(可有多个参数值)(since v3.7) |
NotLike | nk | x not like ? | 是 | 反模糊匹配(since v3.8) |
InList | il / mv | x in (?, ?, ...) | 是 | 多值查询(InList / il 自 v3.3 新增,之前是 MultiValue / mv ) |
NotIn | ni | x not in (?, ?, ...) | 是 | 多值查询(since v3.3) |
IsNull | nl | x is null | 否 | 为空(since v3.3) |
NotNull | nn | x is not null | 否 | 不为空(since v3.3) |
Empty | ey | x is null or x = '' | 否 | 为空(仅适用于 字符串 类型的字段) |
NotEmpty | ny | x is not null and x != '' | 否 | 不为空(仅适用于 字符串 类型的字段) |
如果不够,你还可以自定义哦,参阅:bs.zhxu.cn/guide/lates…
5、新增根参数安全机制
先回顾一下逻辑分组:juejin.cn/post/709241…
当指定了组表达式后,所有不在表达式指定组内的 字段参数 都会被 检索器 忽略,例如:
# A 组
A.name = Jack
# 组外字段参数,当 gexpr 非空且合法时将会被忽略
age = 20
# 组表达式
gexpr = A
但有时候组表达式 gexpr
是需要前端指定的,同时后端也需要注入一些参数,并且 不能被忽略,这时该怎么办呢?此时只需多注入一个根组(用 $
表示)参数即可:
# 组外字段参数,当 gexpr 为空或非法时有效
age = 20
# 根组参数,当 gexpr 非空且合法时有效
$.age = 20
组 $
是框架内置的一个组,它与 gexpr
之间永远是 且
的关系。
为了让某个字段参数不能被忽略,我们必须向检索参数中注入两个参数(如上面的 age
与 $.age
),这略显麻烦。为此,v3.8.0
同时增强了参数构建器,使其 field(..)
方法在 未显示指定组 之前,都默认会添加对应的根组参数。例如:
Map<String, Object> params = MapUtils.builder()
// 未显示指定组 之前调用 field 方法
.field(User::getAge, 20)
// 等效于下面的两行代码:
// .put("age", 20)
// .put("$.age", 20)
.group("A")
// 显示指定组 之后调用 field 方法
.field(User::getName, "Jack")
// 只等效于:
// .put("A.name", "Jack")
.build()
所以当后端需要手动添加检索条件时,我们推荐您使用参数构建器。
6、优化注解省略机制
原本若要在实体类字段上省略 @DbFeild
时,必须满足以下三条件之一:
- 实体类省略了
@SearchBean
注解 - 实体类的
@SearchBean
没有指定tables
属性 - 实体类的
@SearchBean
指定了autoMapTo
属性
在 v3.8.0
之前,tables
与 autoMapTo
必须 同时出现 或 同时消失 才可以省略 @DbFeild
注解,例如改成这样即可:
@SearchBean(tables="user", autoMapTo="user")
public class UserVO {
private Long id; // 省略注解 @DbFeild
private Long name; // 省略注解 @DbFeild
}
但是总有小伙伴,不想不想省略 tables
,又不想指向 autoMapTo
,写成这样:
@SearchBean(tables="user")
public class UserVO {
private Long id; // 省略注解 @DbFeild
private Long name; // 省略注解 @DbFeild
}
结果让不少人都收到了异常,参考:github.com/ejlchina/be…
为了让大家少踩坑,v3.8.0
让这也兼容了这种的写法。现在要省略 @DbFeild
,只需满足以下四个条件之一即可:
- 实体类省略了
@SearchBean
注解 - 实体类的
@SearchBean
没有指定tables
属性 - 实体类的
@SearchBean.tables
只含一张表(since v3.8.0) - 实体类的
@SearchBean
指定了autoMapTo
属性
7、其它变更
其它变更就不一一列列举了,大家可以在这里查看:
未来特性
如果觉得不错,点个 Star 吧:
- Github: github.com/troyzhxu/be…
- Gitee: gitee.com/troyzhxu/be…
请大家继续关注,感谢一路走来支持 Bean Searcher 的你们,我们会努力做到更好。
转载自:https://juejin.cn/post/7116736773065343012