检索示例

查询方法

为便于下文示例讲解,我们首先定义一个 SearchBean,并假设 Bean Searcher 的可配置项都是默认值。

@SearchBean(tables = "user") 
public class User {

    @DbField("id")
    private Long id;        // ID 

    @DbField("name")
    private String name;    // 姓名

    @DbField("age")
    private int age;        // 年零

    @DbField("point")
    private int point;      // 积分

    // Getter and Setter ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

分页查询

不带参数时,默认查询 15 条数据(因为 默认分页大小为 15 ,可配置):

// 执行一条 SQL,默认查询 15 条数据
List<User> users = searcher.searchList(User.class, new HashMap<>());
1
2

查询第 2 页,每页 20 条(page 和 size 的参数名可配置):

Map<String, Object> params = new HashMap<>();
params.put("page", 1);
params.put("size", 20);
// 执行 1 条 SQL
List<User> users = searcher.searchList(User.class, params);
1
2
3
4
5

分页检索时,返回分页信息:

// 执行 2 条 SQL
SearchResult<User> result = searcher.search(User.class, params);
// 用户列表
List<User> users = result.getDataList();
// 数据总条数
Number totalCount = result.getTotalCount();
// 总页数
Number totalPage = result.getTotalPage();
// 当前页数
Number page = result.getPage();
1
2
3
4
5
6
7
8
9
10

全量查询

Sercher 实例的 searchAll 方法可检索满足条件的所有数据:

Map<String, Object> params = new HashMap<>();
// 向 params 添加检索条件, 比如按某个字段检索..
// 执行 1 条 SQL,不分页
List<User> result = searcher.searchAll(User.class, params);
1
2
3
4

对象查询

Sercher 实例的 searchFirst 方法可检索满足条件的第一个数据:

Map<String, Object> params = new HashMap<>();
// 向 params 添加检索条件, 比如按某个字段检索..
// 执行 1 条 SQL,limit 1
User user = searcher.searchFirst(User.class, params);
1
2
3
4

统计查询

单字段统计:

// 执行 1 条 SQL,返回满足条件的用户的总年龄
Number totalAge = searcher.searchSum(User.class, params, "age");
1
2

多字段统计:

// 执行 1 条 SQL
Number[] summaries = searcher.searchSum(User.class, params, new String[] { "age", "point" });
Number totalAge = summaries[0];                 // 满足条件的用户总年龄
Number totalPoint = summaries[1];               // 满足条件的用户总积分
1
2
3
4

分页列表检索时顺带统计:

// 执行 2 条 SQL
SearchResult<User> result = searcher.search(User.class, params, new String[] { "age" });
List<User> users = result.getDataList();        // 用户列表
Number[] summaries = result.getSummaries();     // 统计信息
1
2
3
4

检索方式

不同的检索方式,只是调用 Sercher 实例的检索方法时传递的第二个 Map<String, Object> 类型的参数不同而已,所以为了简化文档,下文示例代码中只列出检索参数的组装过程,不再赘述 Sercher 实例的方法的调用。

精确查询

以例说明,查询 age 等于 18 的用户:

Map<String, Object> params = new HashMap<>();
params.put("age", 18);
params.put("age-op", "eq");
// ...
1
2
3
4

范围查询

以例说明,查询 age 大于 18 的用户:

Map<String, Object> params = new HashMap<>();
params.put("age", 18);
params.put("age-op", "gt");     // gt: 大于,ge: 大于等于,lt: 小于,le: 小于等于,ne: 不等于
// ...
1
2
3
4

模糊查询

以例说明,查询 name 包含字符串 'Alice' 的用户:

Map<String, Object> params = new HashMap<>();
params.put("name", 'Alice');
params.put("name-op", "in");    // in: 包含,sw: 以...开头,ew: 以...结尾
params.put("name-ic", true);    // 同时可以指定是否可以忽略大小写
// ...
1
2
3
4
5

区间查询

以例说明,查询 age 在 18 与 30 之间的用户:

Map<String, Object> params = new HashMap<>();
params.put("age-0", 18);
params.put("age-1", 30);
params.put("age-op", "bt");    // 区间查询,age 字段 2 个值
// 查询在 18 与 30 之间的用户(包含边界) 
1
2
3
4
5

age 字段的参数值只有一个,或第二个参数值为 null 时,Between 运算符查询大于等于第一个参数值的数据:

Map<String, Object> params = new HashMap<>();
params.put("age", 18);
params.put("age-op", "bt");    // 区间查询,name 字段有 1 个值,或第 2 个为 null
// 查询大于等于 18 的用户
1
2
3
4

age 字段的参数值有两个,但 第一个为 null 时,Between 运算符查询小于等于第二个参数值的数据:

Map<String, Object> params = new HashMap<>();
params.put("age-0", null);
params.put("age-1", 30);
params.put("age-op", "bt");    // 区间查询,age 字段有 2 个值,但第 1 个为 null
// 查询小于等于 30 的用户
1
2
3
4
5

age 的参数值超多 2 个时,Between 运算符只使用前两个参数值,会忽略其它参数值。

TIP

Bean Searcher 判断参数值的顺序是根据 age-{n} 里面的序号 n 判断的,与代码的顺序无关。

多值查询

以例说明,查询 name 等于 Jack 和 Tom 和 Alice 的用户:

Map<String, Object> params = new HashMap<>();
params.put("name-0", 'Jack');
params.put("name-1", 'Tom');
params.put("name-2", 'Alice');
params.put("name-op", "mv");        // 多值查询,name 字段有多个值
// ...
1
2
3
4
5
6

非空查询

以例说明,查询 name 不为空的用户:

Map<String, Object> params = new HashMap<>();
params.put("name-op", "ny");    // ey: 为空,ny: 不为空
// ...
1
2
3

动态检索

分表检索

某订单系统的数据量非常巨大,如果把所有数据都放在一张表内,查询时经常会产生一些慢 SQL,检索时间长用户体验非常差。为了缓解这个问题,我们可以按年度把订单数据放在不同的表内,例如:

  • order_2018 存放 2018 年的订单
  • order_2019 存放 2019 年的订单
  • order_2020 存放 2020 年的订单
  • order_2021 存放 2021 年的订单

此时,我们的 SearchBean 可以这样定义:

@SearchBean(
    tables = ":table: o, user u",      // 参数 table 由检索时动态指定
    joinCond = "o.user_id = u.id"      // 实际项目中可能还会关联其它表
)
public class Order {
    
    @DbField("o.id")
    private long id;

    @DbField("o.order_no")
    private String orderNo;

    @DbField("u.username")
    private String username;

    // 其它字段 ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

然后,后台订单管理系统加载数据时,同样只需几行代码便可轻松搞定:

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private Searcher searcher;

    /**
     * 订单检索接口
     * @param year 查询的订单年份,如:2018,2019
     **/
    @GetMapping("/index")
    public Object index(HttpServletRequest request, int year) {
        // 收集其它检索参数
        Map<String, Object> params = MapUtils.flat(request.getParameterMap());
        // 根据年份指定查询订单的表名
        params.put("table", "order_" + year);
        // 开始检索,并返回数据
        return searcher.search(Order.class, params);
    }
	
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

动态字段

参见 实体类 > 嵌入参数 > 嵌入到 @DbField 章节,字段参数 field 同样在检索参数中指定即可。