往期阅读:
Bean Searcher 是一款专注高级查询的只读 ORM 开源项目,其官网如下:
// SpringBoot / Grails 项目直接使用该依赖
implementation 'cn.zhxu:bean-searcher-boot-starter:4.3.0'
// Solon 项目直接使用该依赖(功能同 bean-searcher-boot-starter)
implementation 'cn.zhxu:bean-searcher-solon-plugin:4.3.0'
// 非 SpringBoot / Grails / Solon 项目,直接使用核心依赖,
implementation 'cn.zhxu:bean-searcher:4.3.0'
and(..)
与 or(..)
方法:例如,后端想查询满足 {[(name = 'Jack' 且 忽略大小写) 并且 (gender = 'Male')] 或者 [name = 'Alice' 并且 gender = 'Female']} 并且 (age >= 20)
条件的所有 User
数据,则可以:
// 构造检索参数
var params = MapUtils.builder()
.or(o -> o
.and(a -> a
.field(User::getName, "Jack").ic()
.field(User::getGender, "Male")
)
.and(a -> a
.field(User::getName, "Alice")
.field(User::getGender, "Female")
)
)
.field(User::getAge, "20").op(GreateEqual.class)
.build();
// 执行查询
List<User> users = beanSearcher.searchAll(User.class, params);
使用 and(..)
与 or(..)
方法后,无需在手动调用 groupExpr(..)
设置逻辑表达式,因为它会自动生成了。
// 构造检索参数
var params = MapUtils.builder()
.group("A") // A 组开始
.field(User::getName, "Jack").ic()
.field(User::getGender, "Male")
.group("B") // B 组开始
.field(User::getName, "Alice")
.field(User::getGender, "Female")
.group("C") // C 组开始
.field(User::getAge, "20").op(GreateEqual.class)
.groupExpr("(A|B)&C") // 组间逻辑关系(组表达式)
.build();
// 执行查询
List<User> users = beanSearcher.searchAll(User.class, params);
前端只需传参:
A.name=Jack // A 组条件
A.name-ic=true
A.gender=Male
B.name=Alice // B 组条件
B.gender=Female
C.age=20 // C 组条件
C.age-op=ge
gexpr=(A|B)&C // 组间的逻辑关系
然后,在后端一行代码实现查询:
@GetMapping("/users")
public SearchResult<User> users(HttpServletRequest request) {
// 接收前端参数,执行查询(无需担心非法参数,框架会自动过滤)
return beanSearcher.search(User.class, MapUtils.flat(request.getParameterMap()));
}
参考:https://bs.zhxu.cn/guide/param/group.html
例如,前端传参:
A.name=Jack // A 组条件
B.name=Alice // B 组条件
gexpr=A|B // 组间的逻辑关系
然后,后端也构建了逻辑关系:
@GetMapping("/users")
public SearchResult<User> users(HttpServletRequest request) {
// 接收前端参数
var params = MapUtils.flatBuilder(request.getParameterMap())
// 附加一些条件
.or(o -> o
.field(User::getGender, "Female")
.field(User::getAge, "20").op(LessThan.class)
)
.build();
// 执行查询
return beanSearcher.search(User.class, params);
}
则实际生成的条件为:(name = 'Jack' 或者 name = 'Alice') 并且 (gender = 'Female' 或者 age < 20)
。
参考:https://bs.zhxu.cn/guide/param/group.html#表达式合并-since-v4-3-0
例如在 SearchBean 中定义了一个 ages
拼接参数:
@SearchBean(where = "age in (:ages:)")
public class User {
// ...
}
然后在检索时,可以为其直接添加集合参数值:
var params = MapUtils.builder()
.put("ages", Arrays.asList(20,30,40)) // 直接使用 List 参数值,也可以使用 Set
.build();
List<User> users = searcher.searchList(User.class, params);
也可以使用数组作为参数值:
var params = MapUtils.builder()
.put("ages", new int[] {20,30,40}) // 可以使用原生数组
.put("ages", new Integer[] {20,30,40}) // 也可以使用对象数组
.build();
List<User> users = searcher.searchList(User.class, params);
在 v4.3.0
之前,只能使用字符串:
var params = MapUtils.builder()
.put("ages", "20,30,40") // v4.3.0 之前的用法,只能传字符串
.build();
List<User> users = searcher.searchList(User.class, params);
参考:https://bs.zhxu.cn/guide/param/embed.html#拼接参数
例如,前端这样请求用户查询接口:
age=20
& age=30
& age-op=bt
则其与下面的方式等效:
age-0=20
& age-1=30
& age-op=bt
bt
是什么?请参考:https://bs.zhxu.cn/guide/param/field.html
例如,前端原本的两个参数 age=20 & age-op=ge
,现在可以简化成一个:age-ge=20
。
即使进行了逻辑分组,也可以被简化,例如:
A.age=30
A.age-op=gt
可以简化为
A.age-gt=30
参考:https://bs.zhxu.cn/guide/advance/filter.html#suffixopparamfilter
例如,原来的传参形式(age between 20 and 30):
age-0=20
age-1=30
age-op=bt
现在可以简化为:
age=[20,30]
age-op=bt
当然还可以进一步简化:
age-bt=[20,30]
参考:https://bs.zhxu.cn/guide/advance/filter.html#jsonarrayparamfilter
AlwaysTrue
(恒真:at
)与 AlwaysFalse
(恒假:af
)运算符AlwaysTrue
(恒真:at
)始终生成条件 1
AlwaysFalse
(恒真:af
)始终生成条件 0
例如,前端传参 name=Jack & age = 30
两个参数进行用户查询,后端接口里想忽略前端的 age
字段,则可以:
@GetMapping("/users")
public SearchResult<User> users(HttpServletRequest request) {
var params = MapUtils.flatBuilder(request.getParameterMap())
field(User::getAge).op(AlwaysTrue.class) // 使用 恒真 运算符
.build();
return beanSearcher.search(User.class, params);
}
则,最终生成的 SQL 条件为 ... where name = 'Jack' and 1
,起到吧 age
参数忽略的目的。当然,我们还可以使用 条件约束,直接声明 age
字段不能参与 where 条件,来到达通用的效果:
public class User {
@DbField(conditional = false)
private int age;
// ...
}
只不过使用这种方式,age
字段就再也不能动态作为条件了。所以,这两种方式分别适用于不同的场景。
升级了 OracleDialect
, 采用了 offset ? rows fetch next ? rows only
分页语法,该语法在 Oracle 12c(2013年6月发布)及以上版本支持。
所以如果你使用的是 12c 以前的 Oracle 版本,在使用 Bean Searcher v4.3+ 时,则需要将老版本的 OracleDialect
(点击查看方言代码) 拷贝到自己的项目中,作为自定义方言使用。
参考:https://bs.zhxu.cn/guide/advance/dialect.html
BeanMeta
、SearchSql
与 参数构建器,优化或增加了一些 APIBeanMeta
新增了 getSqlSnippets()
方法,用户可以在 过滤器 与 拦截器 中 使用该方法获取该实体类上所有已解析的 SQL 片段;SearchSql
新增了 getSearchParam()
方法,用户可以在 拦截器 中使用该方法获取到解析后的检索参数;field(FieldFn, Collection)
与 field(String, Collection)
方法,使其第二个参数兼容传入 null
的用法,例如下面的代码将不再报错:// List 参数,但是值为 null,最终该参数条件会被忽略
List<Long> idList = null;
var params = MapUtils.builder()
.field(User::getId, idList).op(InList.class)
.build();
List<User> users = beanSearcher.search(User.class, params);
buildForRpc()
方法,用于在后端请求其它服务的 Bean Searcher 查询接口,例如:// 组织检索参数
var params = MapUtils.builder()
.field(User::getAge, 20, 30).op(Between.class)
.page(0, 20)
.buildForRpc();
// 调用 A 服务中的接口
List<User> users = romoteApi.getUserList(params);
参考:https://bs.zhxu.cn/guide/usage/rpc.html
bean-searcher:
params:
group:
# 指定组表达式是否可合并,默认 true
mergeable: true
filter:
# 是否启用 SizeLimitParamFilter,默认 true
use-size-limit: true
# 是否启用 ArrayValueParamFilter,默认 true
use-array-value: true
# 是否启用 SuffixOpParamFilter,默认 false
use-suffix-op: true
# 是否启用 JsonArrayParamFilter,默认 false
use-json-array: true
参考:https://bs.zhxu.cn/guide/info/versions.html#v4-3-的新特性-v4-3-0
如果觉得文本不错,动手点个赞吧 _
往期阅读:
正常情况下,Spring 容器加载 Bean 的顺序是不确定的,那么我们如果需要按顺序加载 Bean 时应如何操作?本文将详细讲述我们如何才能控制 Bean 的加载顺序。