这篇文章不是严格的教程,是我学习的手记吧,发出来大家共同学习。
概述
今天给大家介绍一个java数据库查询的开源项目,叫BeanSearcher(Apache2.0,国产开源项目)。 我们使用java操作数据库很多时候使用的是mybatis、Hibernate、jpa等等,都是具有完整的CRUD功能的,而BeanSearcher专做查询功能,对多表查询支持的非常好。优点灵活小巧,即可以像使用sql语句查询一样的方便,又保留了ORM的面向对象编程特点。在使用时不必再纠结mybatis的各种映射,do和vo的转换,能够极大的提高我们的开发效率,又不增加维护难度。 官网网址:bs.zhxu.cn
特点&优点
- 集成简单,注解开发,学习难度低,可以很快上手使用
- 支持参数过滤、字段转换器、Select指定字段、SQL拦截
- 可以快速方便的进行多表查询
- 简化了DO/VO的转换
- 扩展性强
- 支持多数据源
在BeanSearcher有MapSearcher和BeanSearcher两个检索器,是我们用来进行查询的最重要的工具,其中MapSearcher查询出的结果都是以Map对象呈现,BeanSearcher的结果以Bean或者叫VO的对象呈现,根据实际开发的需求,我们可以选择不同的检索器。
学习目标
- 单表查询
- 如果使用查询参数
-
- Map硬编码
- MapUtils使用
- 多表查询
-
- 2表联合查询
- 2个以上的表联合查询
- 子查询
- 分组查询
基本功能
项目Demo地址
视频地址
项目集成
首先看一下如何集成BeanSearcher,我们这里已经准备了一个springboot+mybatis的项目,我们将beanSearcher集成上来。
SpringBoot项目只需要添加bean-searcher-boot-starter一个依赖就可以了。
<!-- Bean Searcher 核心依赖,任何框架都可使用该依赖 -->
<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>bean-searcher</artifactId>
<version>3.8.0</version>
</dependency>
<!-- SpringBoot / Grails 的项目直接使用以下依赖,更为方便(只添加这一个依赖即可) -->
<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>bean-searcher-boot-starter</artifactId>
<version>3.8.0</version>
</dependency>
基本配置
如果我们之前使用的mybatis,那么我们的Bean对象命名可能就是UserDO、UserDAO之类的字样,这个时候我们简单的做一些配置,让BeanSearcher忽略这个Bean上多余的字母,让他和我们的数据库表做好映射。
bean-searcher:
sql:
default-mapping:
redundant-suffixes: DO
查询
我们可以直接使用之前mybatis中的DO来进行查询,不需要新建DO。
无参查询
beanSearcher.searchList(UserDO.class,null);
可以使用searchFirst来查询一个数据,如果查询出多个结果是不会抛异常的。
beanSearcher.searchFirst(UserDO.class,null);
查询参数的使用
通过构造一个Map进行查询参数
public List<UserDO> userList( String name, Integer age){
Map<String,Object> params = new HashMap<>();
params.put("name",name);
params.put("age",age);
params.put("age-op","gt");
return beanSearcher.searchList(UserDO.class,params);
}
//
这里有一个age-op
是来设置查询age
是的运算符,(这里的op,可以通过参数进行设置),默认每个参数都会有一个eq
的运算符,我们在这里设置了age
字段的运算符是>
。
使用MapUtils简化查询参数
很明显这么查询也会很慢,这里我们就可以来看看BeanSearcher给我们提供的MapUtils了,使用这个工具可以更方便的进行查询参数的拼装工作。
@RequestMapping(value = "users2")
public List<UserDO> userList2( String name, Integer age){
return beanSearcher.searchList(UserDO.class,
MapUtils.builder()
.field(UserDO::getName,name).op(Equal.class)
.field(UserDO::getAge,age).op(GreaterThan.class)
.build()
);
}
MapUtils.flat一行代码实现查询参数
虽然代码很多,但是好消息是我们变成了一行,而且我们不用再去拼接字段名了,一切看起来都挺顺利。那如果这个时候产品经理说了用户名称需要进行模糊查询怎么办,又要改界面,加参数。不不不~~MapUtils还有办法。
@RequestMapping(value = "users3")
public List<UserDO> userList3(HttpServletRequest request){
return beanSearcher.searchList(UserDO.class,
MapUtils.flat(request.getParameterMap())
);
}
通过flat方法将一个 value 为数组的 Map 对象,拉平为 value 为单值的 Map 对象,我们这里直接将界面上的参数map传过去,然后对我们界面的入参调整一下,完成了,以后再加什么参数都无所谓啦,只需要调整前端入参就可以了。
age=32&name=mi&age-op=gt&name-op=ct
分页查询
不需要额外的配置天生支持分页查询,只需要我们传递page和size做为查询参数就可以了。分页数据的返回需要我们自己处理一下,简单的写了一下,具体的封装工作根据各位项目的实际情况处理吧。
public Page<StatisticVO> statisticPage(HttpServletRequest request){
Map<String,Object> where = MapUtils.flat(request.getParameterMap());
SearchResult<StatisticVO> search = beanSearcher.search(StatisticVO.class,
where);
Page<StatisticVO> page = new Page<>();
page.setTotal(search.getTotalCount().longValue());
page.setRecords(search.getDataList());
page.setCurrent(Long.valueOf(where.get("page").toString()));
return page;
}
多表查询
先来看看数据库结构
两个表
班级统计 | |
---|---|
学校 | 班级 |
@SearchBean(
tables = "school s , class c",
where = "s.id = c.school_id",
autoMapTo = "c"
)
@Data
public class StatisticVO {
@DbField("s.name")
String name;
String grade;
String num;
}
@RequestMapping(value ="statistic")
public List<StatisticVO> statistic(HttpServletRequest request){
return beanSearcher.searchList(StatisticVO.class, MapUtils.flat(request.getParameterMap()));
}
多个表
班级统计 | ||
---|---|---|
学校 | 班级 | 学生 |
@SearchBean(
tables = "school s " +
"left join class c on c.school_id = s.id " +
"left join student st on st.class_id = c.id",
autoMapTo = "c"
)
@Data
public class Statistic1VO {
@DbField("s.name")
String name;
String grade;
String num;
@DbField("st.name")
String studentName;
}
@RequestMapping(value ="statistic1")
public List<Statistic1VO> statistic1(HttpServletRequest request){
return beanSearcher.searchList(Statistic1VO.class, MapUtils.flat(request.getParameterMap()));
}
子查询
班级统计 | |||
---|---|---|---|
学校 | 班级 | 学生 | 总成绩 |
@SearchBean(
tables = "school s " +
"left join class c on c.school_id = s.id " +
"left join student st on st.class_id = c.id",
autoMapTo = "c"
)
@Data
public class Statistic1VO {
@DbField("s.name")
String name;
String grade;
String num;
@DbField("st.name")
String studentName;
}
@RequestMapping(value ="statistic2")
public List<Statistic2VO> statistic2(HttpServletRequest request){
return beanSearcher.searchList(Statistic2VO.class, MapUtils.flat(request.getParameterMap()));
}
分组查询
统计一下每个班级的成绩,要求根据批次和科目统计
班级统计 | ||
---|---|---|
学校 | 班级 | 总成绩 |
@SearchBean(
tables = "performance p " +
"left join student stu on stu.id = p.student_id " +
"left join class c on c.id = stu.class_id " +
"left join school sc on sc.id = c.school_id",
autoMapTo = "c",
groupBy = "sc.name,c.grade,c.num"
)
@Data
public class Statistic3VO {
@DbField("sc.name")
String name;
@DbField("concat(c.grade,'年',c.num,'班')")
String gradeClass;
@DbField("sum(p.score)")
Double sumScore;
}
public List<Statistic3VO> statistic3(HttpServletRequest request){
return beanSearcher.searchList(Statistic3VO.class, MapUtils.flat(request.getParameterMap()));
}
\
需要了解的参数配置
排序约束配置
配置键名 | 含义 | 可选值 | 默认值 |
---|---|---|---|
bean-searcher.sql.default-mapping.sort-type | 默认排序约束 | ALLOW_PARAM、ONLY_ENTITY | ALLOW_PARAM |
表名注解缺省配置
配置键名 | 含义 | 可选值 | 默认值 |
---|---|---|---|
bean-searcher.sql.default-mapping.table-prefix | 表名前缀 | 字符串 | null |
bean-searcher.sql.default-mapping.underline-case | 表名和字段名是否驼峰转小写下划线(since v3.7.0) | 布尔值 | true |
bean-searcher.sql.default-mapping.upper-case | 表名和字段名是否大写 | 布尔值 | false |
bean-searcher.sql.default-mapping.redundant-suffixes | 类名的冗余后缀(可配多个)(since v3.3.0) | 冗余后缀 | null |
默认继承方式配置
配置键名 | 含义 | 可选值 | 默认值 |
---|---|---|---|
bean-searcher.sql.default-mapping.inherit-type | 默认继承类型 | ALL、TABLE、FIELD、NONE | ALL |
全局属性忽略配置
配置键名 | 含义 | 可选值 | 默认值 |
---|---|---|---|
bean-searcher.sql.default-mapping.ignore-fields | 需要全局忽略的属性名(可指定多个) | 字符串数组 | null |
分页参数配置
配置键名 | 含义 | 可选值 | 默认值 |
---|---|---|---|
bean-searcher.params.pagination.type | 分页类型 | page、offset | page |
bean-searcher.params.pagination.default-size | 默认每页查询条数 | 正整数 | 15 |
bean-searcher.params.pagination.max-allowed-size | 每页最大查询条数(分页保护) | 正整数 | 100 |
bean-searcher.params.pagination.page | 页码参数名(在 type = page 时有效) | 字符串 | page |
bean-searcher.params.pagination.size | 每页大小参数名 | 字符串 | size |
bean-searcher.params.pagination.offset | 偏移参数名(在 type = offset 时有效) | 字符串 | offset |
bean-searcher.params.pagination.start | 起始页码 或 起始偏移量 | 自然数 | 0 |
排序参数配置
配置键名 | 含义 | 可选值 | 默认值 |
---|---|---|---|
bean-searcher.params.sort | 排序字段参数名 | 字符串 | sort |
bean-searcher.params.order | 排序方法参数名 | 字符串 | order |
bean-searcher.params.order-by | 排序参数名(since v3.4.0) | 字符串 | orderBy |
字段参数配置
配置键名 | 含义 | 可选值 | 默认值 |
---|---|---|---|
bean-searcher.params.separator | 字段参数名分隔符 | 字符串 | - |
bean-searcher.params.operator-key | 字段运算符参数名后缀 | 字符串 | op |
bean-searcher.params.ignore-case-key | 是否忽略大小写字段参数名后缀 | 字符串 | ic |
逻辑分组参数配置
配置键名 | 含义 | 可选值 | 默认值 |
---|---|---|---|
bean-searcher.params.group.enable | 是否开启逻辑分组功能 | 布尔 | true |
bean-searcher.params.group.expr-name | 逻辑表达式参数名 | 字符串 | gexpr |
bean-searcher.params.group.expr-cache-size | 表达式解析缓存(个数) | 整型 | 50 |
bean-searcher.params.group.separator | 组名分隔符 | 字符串 | . |
指定Select字段配置
配置键名 | 含义 | 可选值 | 默认值 |
---|---|---|---|
bean-searcher.params.only-select | onlySelect 参数名 | 字符串 | onlySelect |
bean-searcher.params.select-exclude | selectExclude 参数名 | 字符串 | selectExclude |
SQL方言配置
配置键名 | 含义 | 可选值 | 默认值 |
---|---|---|---|
bean-searcher.sql.dialect | SQL 方言 | MySQL、Oracle、PostgreSQL、SqlServer | MySQL |
慢SQL日志与监听
配置键名 | 含义 | 类型 | 默认值 |
---|---|---|---|
bean-searcher.sql.slow-sql-threshol | 慢 SQL 阈值(单位:毫秒) | int | 500 |
\
字段运算符
运算符 | 缩写 | 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 != '' | 否 | 不为空(仅适用于 字符串 类型的字段) |