检索实体类

检索实体类,即是被 @SearchBean 注解的类,又称 SearchBean上一节,我们体验了 Bean Searcher 的单表检索功能,但相比于传统的 ORM,其实它更擅长处理复杂的联表检索 与 一些奇奇怪怪的 子查询,此时 SearchBean 的定义也非常容易。

注意

实体类必须定义在 集成 Bean Searcher 时指定的 包路径 下。

多表关联

注解 @SearchBeantables 属性,可以很容易的指定多张表的关联关系。

内连接

@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

或者:

@SearchBean(tables = "user u inner join role r on u.role_id = r.id") 
public class User {
    // ...
}
1
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

右连接

@SearchBean(tables = "user_detail d right join user u on u.id = d.user_id")
public class User {
    // ...
}
1
2
3
4

From 子查询

@SearchBean(
    tables = "(select id, name from user) t"
) 
public class User {
    // ...
}
1
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

其它形式

除了上述的多表关联外,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

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

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

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

嵌入参数

检索实体类 除了可以实现上述的各种形式的 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

示例(动态指定检索表名):

@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

参考:示例 > 动态检索 > 分表检索 章节。

嵌入到 @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

示例(只查指定某些年龄的学生):

@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

嵌入到 @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

嵌入到 @DbField

动态指定检索字段

@SearchBean(tables = "sutdent") 
public class StuAge {

    @DbField(":field:")
    private String value;

}
1
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

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

逻辑字段

利用这个机制,我们可以在 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