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 。