检索实体类
检索实体类,即是被 @SearchBean
注解的类,又称 SearchBean,上一节,我们体验了 Bean Searcher 的单表检索功能,但相比于传统的 ORM,其实它更擅长处理复杂的联表检索 与 一些奇奇怪怪的 子查询,此时 SearchBean 的定义也非常容易。
注意
实体类必须定义在 集成 Bean Searcher 时指定的 包路径 下。
多表关联
注解 @SearchBean
的 tables
属性,可以很容易的指定多张表的关联关系。
内连接
@SearchBean(
tables = "user u, role r", // 两表关联
joinCond = "u.role_id = r.id"
)
public class User {
@DbField("u.name")
private Long username;
@DbField("r.name")
private String rolename;
// Getter and Setter ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
或者:
@SearchBean(tables = "user u inner join role r on u.role_id = r.id")
public class User {
// ...
}
1
2
3
4
2
3
4
左连接
@SearchBean(tables = "user u left join user_detail d on u.id = d.user_id")
public class User {
@DbField("u.name")
private Long username;
@DbField("d.address")
private String address;
// Getter and Setter ...
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
右连接
@SearchBean(tables = "user_detail d right join user u on u.id = d.user_id")
public class User {
// ...
}
1
2
3
4
2
3
4
From 子查询
@SearchBean(
tables = "(select id, name from user) t"
)
public class User {
// ...
}
1
2
3
4
5
6
2
3
4
5
6
关联 From 子查询
@SearchBean(
tables = "user u, (select user_id, ... from ...) t",
joinCond = "u.id = t.user_id"
)
public class User {
// ...
}
1
2
3
4
5
6
7
2
3
4
5
6
7
其它形式
除了上述的多表关联外,Bean Searcher 还支持很多复杂的 SQL 形式:
Select 子查询
@SearchBean(tables = "student s")
public class Student {
@DbField("s.name")
private String name;
// 该学生的总分数(Select 子查询)
@DbField("select sum(sc.score) from student_course sc where sc.student_id = s.id")
private int totalScore;
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Where 子查询
@SearchBean(
tables = "student s",
// 只查询考试均分及格的学生(Where 子查询)
joinCond = "(select avg(sc.score) from student_course sc where sc.student_id = s.id) >= 60"
)
public class GoodStudent {
@DbField("s.name")
private String name;
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Distinct 去重
// 参与考试的课程
@SearchBean(
tables = "student_course sc, course c",
joinCond = "sc.course_id = c.id",
distinct = true // 去重
)
public class ExamCourse {
@DbField("c.id")
private String courseId;
@DbField("c.name")
private String courseName;
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Group By 分组 与 聚合函数
@SearchBean(
tables = "student_course sc, course c",
joinCond = "sc.course_id = c.id",
groupBy = "c.id" // 按课程 ID 分组
)
public class CourseScore {
@DbField("c.id")
private long courseId;
@DbField("avg(sc.score)") // 该课程的平均分(聚合函数:avg)
private long totalScore;
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
嵌入参数
检索实体类 除了可以实现上述的各种形式的 SQL 以外,还可以在注解 @SearchBean
与 @DbField
的 SQL 片段内嵌入 动态 参数。
使用场景
- 动态指定查询的表字段 或 动态指定查询的数据库表名
- 想按某个表字段检索,但又不想把该表字段做成实体类的字段属性
参数类型
实体类的注解内可以嵌入两种形式的参数:
- 形如
:name
的可作为 JDBC 参数的 普通内嵌参数(该参数无 SQL 注入风险,应首选使用) - 形如
:name:
的 拼接参数(该参数会拼接在 SQL 内,开发者在检索时应检查该参数值的合法性,防止 SQL 注入)
嵌入到 @SearchBean.tables
示例(按某字段动态检索):
@SearchBean(
tables = "(select id, name from user where age = :age) t" // 参数 age 的值由检索时动态指定
)
public class User {
@DbField("t.id")
private long id;
@DbField("t.name")
private String name;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
示例(动态指定检索表名):
@SearchBean(
tables = ":table:" // 参数 table 由检索时动态指定,这在分表检索时非常有用
)
public class Order {
@DbField("id")
private long id;
@DbField("order_no")
private String orderNo;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
参考:示例 > 动态检索 > 分表检索 章节。
嵌入到 @SearchBean.joinCond
示例(只查某个年龄的学生):
@SearchBean(
tables = "student",
joinCond = "age = :age"
)
public class Student {
@DbField("s.name")
private String name;
// ...
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
示例(只查指定某些年龄的学生):
@SearchBean(
tables = "student",
joinCond = "age in (:ages:)" // 参数 ages 形如:"18,20,25"
)
public class Student {
@DbField("s.name")
private String name;
// ...
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
嵌入到 @SearchBean.groupBy
动态指定分组条件:
@SearchBean(
tables = "student",
groupBy = ":groupBy:" // 动态指定分组条件
)
public class StuAge {
@DbField("avg(age)")
private int avgAge;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
嵌入到 @DbField
动态指定检索字段
@SearchBean(tables = "sutdent")
public class StuAge {
@DbField(":field:")
private String value;
}
1
2
3
4
5
6
7
2
3
4
5
6
7
为 Select 子查询动态指定条件
@SearchBean(tables = "student s")
public class Student {
@DbField("s.name")
private String name;
// 查询某一个科目的成绩(具体哪门科目在检索时有参数 courseId 指定
@DbField("select sc.score from student_course sc where sc.student_id = s.id and sc.course_id = :courseId")
private int score;
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
BeanAware
接口 BeanAware
是 Search Bean 的可选实现接口。实现这个接口的 SearchBean,可以在 afterAssembly 方法里添加 Bean 装配完之后的自定义逻辑。
组装监听
例如
@SearchBean(tables = "user")
public class User implements BeanAware {
@DbField("id")
private Long id;
@DbField("name")
private String name;
@Override
public void afterAssembly() {
// 该方法会在 User 的字段值装配完后自动执行
// 代码走到这里说明,说明被 @DbField 注解的字段都已经被赋过值
System.out.println("id = " + id + ", name = " + name);
}
// Getter and Setter ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
逻辑字段
利用这个机制,我们可以在 Search Bean 内自定义一些与数据库表无直接映射关系的逻辑字段,例如
@SearchBean(tables = "user")
public class User implements BeanAware {
@DbField("age")
private int age;
// 表示该用户是否是一个老人
private boolean older
@Override
public void afterAssembly() {
older = age > 60; // 大于 60 岁,则 older 等于 true
}
// Getter and Setter ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15