问题描述

在 Mybatis 中使用 PageHelper 插件可以轻易的实现分页查询,但是对于某些时候,从库中直接返回的 Po 对象并不是最终需要返回的类型,即分页中的结果集需要进行类型转换,当然,转换过程中不能丢失分页信息。

分析

通过观察 PageHelper 插件的源码可以知道,其分页结果为 Page 对象,继承了  ArrayList 对象,其结果集通过泛型来约束类型。而实际上,java 中的泛型类型约束偏向于编译器层面,即由编译器对泛型进行类型检查,一旦编译通过,最终会转换为原始类型,称之为类型(泛型)擦除,正是有这样的机制存在,此处可以较为简单的实现对分页结果的类型转换。

解决方案

思路

  1. 启动分页器,执行查询 sql 执行分页,设定结果集类型未 T
  2. 提取分页结果并进行类型转换,设转换类型为 E,保存结果为 resultList<E>) 变量;
  3. 对分页对象 Page<T>  执行其继承自父类的  ArrayList#clear() 方法,清除原结果集;
  4. Page<T> 对象进行显示的强转,转换结果类型与 resultE) 变量保持一致,此处会出现编译器警告,但实际上由于类型擦除的机制存在,在编译后的类型始终是原始类型 Page,(且已经提前清空了强转之前的泛型类型对象)因此此处忽略警告对编译结果不会造成异常影响;
  5. 最后将转换的结果  result  置入强转后的  Page<E>  对象中。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class PageHelpers<K, E> {

private Page<E> page;

/**
* 开始分页
*
* @param pageNum 页码
* @param pageSize 每页显示数量
* @param execute 查询 sql
* @param mapper 结果集转换方法
* @return 返回 {@code PageHelpers}
*/
@SuppressWarnings("unchecked")
public PageHelpers<K, E> startPage(int pageNum, int pageSize,
Supplier<?> execute,
Function<? super K, ? extends E> mapper
) {
Page<K> origin = PageHelper.startPage(pageNum, pageSize);
// 必须使用函数参数,来保住查询 sql 是紧跟分页器之后执行
// 否则,分页器无法捕捉查询结果集
execute.get();
// 转换结果集类型
List<E> result = origin.getResult().stream().map(mapper).collect(Collectors.toList());
origin.clear();
// 泛型仅作编译时类型约束,编译后统一转换为最小继承的类型,
// 此处未限定,即转换为 Object,因此当确保使用时类型正确,是可以进行强转的
page = (Page<E>) origin;
page.addAll(result);
return this;
}

/**
* 获取分页信息
*
* @return {@code PageInfo}
*/
public PageInfo<E> getPageInfo() {
return get().toPageInfo();
}

/**
* 返回分页对象
*
* @return {@code Page}
*/
public Page<E> get() {
return page;
}

其他

当然,使用  BeanUtils#copyProperties()  复制对象的属性,或者使用反射提取其字段,都是可以实现对分页结果的类型转换。

评论