Mybatis(三)
五、分页查询
在 MySQL 中存在一个特殊查询,就是分页查询。在 Mybatis 的中存在一个万能的 Map 类型,可以解决很多问题。
5.1 使用 limit 实现分页
我们在 UserMapper 的接口中添加一个方法:
/**
* 分页查询
*
* @param map 分页参数
* @return User 集合
*/
List getUserByLimit(Map map);
在 mapper 文件中添加下面语句:
测试类和结果:
@Test
public void test() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap map = new HashMap();
map.put("startIndex", 0);
map.put("pageSize", 2);
List userByLimit = mapper.getUserByLimit(map);
for (User user : userByLimit) {
System.out.println(user);
}
sqlSession.close();
}
注意我们开启了标准日志输出(STDOUT_LOGGING)
5.2 使用 RowBounds
除了 limit 你还可以使用 RowBounds 对象进行分页查询。但是由于它是在 sql 查询出所有结果的基础上截取数据的,所以在数据量大的sql中并不适用,它更适合在返回数据结果较少的查询中使用。
最核心的是在 mapper 接口层,传参时传入 RowBounds(int offset, int limit) 对象,即可完成分页。
mapper 接口层代码:
/**
* 实现分页查询
*
* @param map 万能的 map
* @param rowBounds 分页对象
* @return User 集合
*/
List getUserByRowBounds(Map map, RowBounds rowBounds);
xml 映射代码:
测试代码和结果:
@Test
public void test2() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap map = new HashMap<>();
map.put("name", "");
List users = mapper.getUserByRowBounds(map, new RowBounds(0, 3));
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
六、进阶查询
细心的同学已经发现了,我们在使用 RowBounds 对象实现分页时,使用了一个 resultMap 标签。
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets
数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。
实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap
能够代替实现同等功能的数千行代码。
实际上之前你写的所有 select 语句都是一个 resultMap 。只是它们没有显式的指定 resultMap。
当你没有按照规范(数据库字段和 JavaBean 属性不一致),你可以使用 resultMap 来指定 JavaBean 属性和数据库字段对应。它就和 SQL 取别名的效果一致。
如果世界总是这么简单就好了
在此之前,我们介绍以下 resultMap 常见的属性
id
– 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能result
– 注入到字段或 JavaBean 属性的普通结果association
– 一个复杂类型的关联;许多结果将包装成这种类型- 嵌套结果映射 – 关联可以是
resultMap
元素,或是对其它结果映射的引用
- 嵌套结果映射 – 关联可以是
collection
– 一个复杂类型的集合- 嵌套结果映射 – 集合可以是
resultMap
元素,或是对其它结果映射的引用
- 嵌套结果映射 – 集合可以是
6.1 搭建环境
在进行进阶查询之前,我们首先搭建一下需要用的环境。需要使用的 MySql:
# 老师表
create table teacher(
id int(10) not null primary key,
name varchar(30) default null
) engine = innodb
default charset = utf8;
#学生表
create table student(
id int(10) not null primary key,
name varchar(30) default null,
tid int(10) default null,
key fktid (tid),
constraint fktid foreign key (tid) references teacher (id)
) engine = innodb
default charset = utf8;
insert into teacher (id, name)
values (1, '王含笑'),
(2, '王卉');
insert into student (id, name, tid)
values (1, '曹超', 1),
(2, '陈浩楠', 1),
(3, '程晨', 1),
(4, '王佳琪', 2),
(5, '程中星', 1);
新建两个实体类:
/**
* 学生表对应的实体类
*
* @author Reimu
*/
@Data
public class Student {
private int id;
private String name;
/**
* 学生需要关联一个老师
*/
private Teacher teacher;
}
/**
* 老师表对应的实体类
*
* @author Reimu
*/
@Data
public class Teacher {
private int id;
private String name;
}
6.2 多对一处理
我们尝试一下查询上述所有学生的信息,以及对应老师的信息!在 MySql 中我们可以直接使用下面的语句进行查询:
select s.id, s.name, t.name
from student s,
teacher t
where s.tid = t.id;
但是这存在一些问题:
可以看见位于 teacher 等号右边的值为 null
这达不到我们的要求。我们想要的是学生和它对应的老师一起被查询出来。
6.2.1 第一种思路
这一种思路是我们完全在使用 mapper 映射文件的特性。尤其是 resultMap 这一个标签。
这有点像子查询的思想一层嵌套一层,我们用 resultMap 将 getTeacher 方法嵌套进了 getStudent 方法中。这使得我们的代码可以实现以下内容:
6.2.2 第二种思路
这是一种根据结果进行查询的方法,我们首先需要知道能完成目标的 sql 语句。然后根据结果一步一步映射:
上面的方法类似于 sql 中的聊表查询。注意当你查询的对象是一个对象时,需要使用 association。
6.3 一对多处理
我们修改一下原来的实体类:
/**
* 学生表对应的实体类
*
* @author Reimu
*/
@Data
public class Student {
private int id;
private String name;
private int tid;
}
/**
* 老师表对应的实体类
*
* @author Reimu
*/
@Data
public class Teacher {
private int id;
private String name;
/**
* 一个老师拥有多个学生
*/
private List students;
}
现在我们需要获取指定老师下的所有学生和老师的信息。很显然普通的 resultType 不能满足我们的要求。我们需要使用 resultMap 来帮助我们实现这一功能。
6.3.1 第一种思路
现在我们写出这一段 sql:
select s.id sid, s.name sname, t.id tid, t.name tname
from kimari.student s,
kimari.teacher t
where s.tid = t.id
and t.id = tid;
根据按结果嵌套查询的思想:
需要注意的是 Teacher 类中属性 students 是一个集合类型,我们需要使用 collection 。对于一个泛型我们使用 ofType 来遍历它里面的每一个单位。
6.3.2 第二种思路
这就是一种相关子查询的思路:
这里还有一点小小的问题,老师的 id 为 0 。这需要我们显式的设置查询老师的 id 。