几个重要的类
PagingResult
分页结果的封装,结果(List)+分页信息。
Search
查询相关的类,封装查询条件+分页信息(当前页和每页记录数)。
BaseMybatisDAO
分页查询相关方法。
前端分页显示
通过PagingResult中的当前页码数
、总页码数
、每页显示数
来计算最大显示页码
。
下面通过一些逻辑,来制定第一页
、最后一页
、中间页
的显示。
<#macro paging pagingList url>
<#--计算最大页码-->
<#if pagingList.total % pagingList.pageSize == 0>
<#assign maxPageIndex = pagingList.total / pagingList.pageSize>
<#else>
<#assign maxPageIndex = (pagingList.total / pagingList.pageSize)?floor + 1>
</#if>
<div class="page">
<#--第一页,禁用“上一页”按钮-->
<#if pagingList.total == 0 || pagingList.page == 1>
<span class="prev-disabled">上一页</span>
<#else>
<#if pagingList.page == 2>
<a href="${url}">上一页</a>
<#else>
<a href="${url}/${pagingList.page-1}">上一页</a>
</#if>
</#if>
<#--第一页-->
<#if (pagingList.total > 0)>
<a href="${url}" <#if pagingList.page == 1>class="current_page"</#if>>1</a>
</#if>
<#--如果不只有一页-->
<#if (maxPageIndex > 1)>
<#--如果当前页往前查3页不是第2页-->
<#if ((pagingList.page - 3) > 2)>
<span class="text">…</span>
</#if>
<#--当前页的前3页和后3页-->
<#list (pagingList.page - 3)..(pagingList.page + 3) as index>
<#--如果位于第一页和最后一页之间-->
<#if (index > 1) && (index < maxPageIndex)>
<a href="${url}/${index}" <#if pagingList.page == index>class="current_page"</#if>>${index}</a>
</#if>
</#list>
<#--如果当前页往后查3页不是倒数第2页-->
<#if (pagingList.page + 3) < (maxPageIndex - 1)>
<span class="text">…</span>
</#if>
<#--最后页-->
<a href="${url}/${maxPageIndex}" <#if pagingList.page == maxPageIndex>class="current_page"</#if>>${maxPageIndex}</a>
</#if>
<#--最后页,禁用“下一页”按钮-->
<#if pagingList.total == 0 || pagingList.page == maxPageIndex>
<span class="prev-disabled">下一页</span>
<#else>
<a href="${url}/${pagingList.page+1}">下一页</a>
</#if>
</div>
</#macro>
(1)第一页:禁用上一页。
(2)最后一页:禁用下一页。
(3)中间页(相差两端5页以上):显示...
。
PS:这里首页为不带页码的URL。
后台代码逻辑
Controller
@RequestMapping(value = "/category/{urlName}/{page}", method = RequestMethod.GET)
public String listDealsOfDealCategory(@PathVariable String urlName, @PathVariable Integer page, Model model,
HttpServletRequest request) {
DealCategory dealCategory = dealCategoryService.getByUrlName(urlName);
model.addAttribute("dealCategory", dealCategory);
PagingResult<Deal> pageResult = dealService.getDealsOfCategories(dealCategory.getSelfAndChildrenIds(),
// getAreaId(request), page, DealConstant.DEAL_NUM_PER_PAGE_IN_DEALS_OF_CATEGORY_PAGE);
getAreaId(request), page, 6);
model.addAttribute("pagingDealList", pageResult);
return "/deal/category";
}
说明:
(1)先拿到分类名称,根据分类名称进行查询,需要注意的是:包括他的孩子节点(细分的类别)。
(2)关键方法dealService.getDealsOfCategories(dealCategory.getSelfAndChildrenIds(),getAreaId(request), page, 6);
获取PagingResult<Deal>
。
(3)拿到封装后的对象,添加到模型里面供前端使用。
Service
public PagingResult<Deal> getDealsOfCategories(List<Long> categoryIds, Long areaId, int page, int rows) {
Search search = new Search();
List<Condition> conditionList = new ArrayList<>();
conditionList.add(new Condition("publishStatus", DealConstant.DEAL_PUBLISH_STATUS_PUBLISH)); // 发布状态为已发布
conditionList.add(new Condition("categoryIdList", categoryIds));
conditionList.add(new Condition("nowTime", new Date()));
conditionList.add(new Condition("areaId", areaId));
search.setConditionList(conditionList);
search.setPage(page);
search.setRows(rows);
return dealDAO.getDealsOfCategories(search);
}
说明:
(1)构建Search
查询类,封装查询条件(ConditionList)和分页信息。
(2)构建查询条件,发布状态、类别ID、时间、地区ID。
(3)数据库查询,返回封装的结果。
DealDAO
public PagingResult<Deal> getDealsOfCategories(Search search) {
return super.findForPage(MAPPER_NAMESPACE + ".countDealsOfCategories", MAPPER_NAMESPACE
+ ".selectDealsOfCategories", search);
}
说明:调用父类的findForPage
方法,第一个sqlId为计算记录总数,第二个sqlId为查询相应的记录。
疑问:分页必须查询所有的记录吗?表小一点还好,如果表很大,岂不是内存爆了。
BaseMybatisDAO
public <T extends BaseEntity> PagingResult<T> findForPage(String countSqlId, String sqlId, Search search) {
RowBounds rowBounds = new RowBounds(search.getFirstRowNum(), search.getRows());
List<T> list = template.selectList(sqlId, getConditionMap(search), rowBounds);
return new PagingResult<>(count(countSqlId, search), list, search.getPage(), search.getRows());
}
// 不用Search条件如何分页
public <T extends BaseEntity> PagingResult<T> findForPage(String countSqlId, String sqlId, int page, int rows, Map<String, Object> params) {
RowBounds rowBounds = new RowBounds((page - 1) * rows, rows);
List<T> data = template.selectList(sqlId, params, rowBounds);
return new PagingResult<>(count(countSqlId, params), data, page, rows);
}
说明:
(1)RowBounds
为MyBatis自带的分页处理工具,对于index之前的采取skip操作,采用limit操作限制数量。
(2)getConditionMap
只是把封装在search中的ConditionList转换为Map。
(3)template.selectList
查询所有语句,附带参数rowBounds
,进行分页查询。【解答了上面的疑问。。。】
(4)封装一个分页结果,count(countSqlId, search)
利用数据库查询总数,封装当前结果集、当前页码、显示数量。
(5)重点是RowBounds
,即使不用Search也可以通过上面第二个方法进行分页查询,根据RowBounds
的参数进行构造。
(6)也可以不查询总量,既返回值中构造的对象第一个参数为0
,因为总量太大,前端没有显示的必要。