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 。