采用MVC结构的Java Web Application免不了遇到很普遍的分页问题,我们可以用一些小技巧来统一处理。
场景
前端请求时,对于列表数据,需要展示当前页编号、总页数、页大小、对象总个数等信息,因此需要后端对从DB获取的数据进行包装。
DAO
以MyBatis为例,从DB读取的列表数据通常是List
,且可手动指定页数和页大小。1
2
3
4
5
6"SELECT * FROM t_user WHERE enable_flag = 1 LIMIT #{offset}, #{size}") (
({
"id", column = "id"), (property =
"name", column = "name") (property =
})
List<User> getPaged(@Param("offset") Integer offset, @Param("size") Integer size);
包装List
我们需要对List进行统一的包装处理,定义一个泛型类PagedList
: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
37public class PagedList <T> {
private List<T> list;
private int total;
private int pageNumber;
private int pageSize;
private int totalPage;
public PagedList(List<T> list, int total, int pageNumber, int pageSize) {
this.list = list;
this.total = total;
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.totalPage = total/pageSize;
}
public static <T> PagedList<T> of(List<T> list, int total) {
return new PagedList<>(list, total);
}
public static <T> PagedList<T> of(List<T> list, int total, int pageNumber, int pageSize) {
return new PagedList<>(list, total, pageNumber, pageSize);
}
public static <T> PagedList empty() {
return null;
}
public <R> PagedList<R> map(Function<T, R> func) {
if(null == list) {
return empty();
}
return PagedList.of(list.stream().map(func).collect(Collectors.toList()), total);
}
//getters & setters
...
}
从DB中获取的列表数据存放在List<T> list
中,且从构造方法的参数中即可得到当前页编号、总页数、页大小、对象总个数等。而map方法,下文会细讲。
Service
在Service层,只需额外获取对象总数即可构造出PagedList对象
。1
2
3
4
5public PagedList<User> getAll(Integer offset, Integer size) {
List<User> users = userDAO.getPaged(offset, size);
int total = userMapper.countAll();
return PagedList.of(users, total);
}
Controller
在Controller层,通常直接返回该PagedList
即可。1
2
3
4
5
6 (method = RequestMethod.GET)
public PagedList<User> getRoles(
@RequestParam(name = "pageNumber", required = false) Integer offset,
@RequestParam(name = "pageSize", required = false) Integer size) {
return userService.getAll(offset, size);
}
Converter
但通常我们不会直接返回从DB中获取的原始数据,还需要对其进一步处理,如将上述获取的用户列表中用户对象的ID隐去,因此需要一个Converter
。
返回给前端的数据中不包含用户的ID,如下:
1 | public class UserResponse { |
而Converter中的方法是一个Wrapper:1
2
3
4
5
6
7
8
9public class Converter {
public UserResponse toResponse(User user) {
UserResponse userResponse = new UserResponse();
if(null != user){
userResponse.setName(user.getName());
}
return userResponse;
}
}
在Controller中使用lambda表达式对列表中的所有对象统一转换,而上文中提到的PagedList
的map
方法则对批量转换提供了支持。1
2
3
4
5
6
7
8
9
private Converter converter;
(method = RequestMethod.GET)
public PagedList<User> getRoles(
@RequestParam(name = "pageNumber", required = false) Integer offset,
@RequestParam(name = "pageSize", required = false) Integer size) {
return userService.getAll(offset, size).map(converter::toResponse);
}
1 | public <R> PagedList<R> map(Function<T, R> func) { |