概述
这里我们只讲常用方式PageHelper的静态方法startPage的执行流程
一、spring容器初始化时,由于我们配置了插件PageHelper给sqlSessionFactoryBean,所以初始化工厂时会为我们载入配置
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource da) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(da);
//设置分页的拦截器
PageInterceptor pageInterceptor = new PageInterceptor();
//创建插件需要的参数集合
Properties properties = new Properties();
//配置数据库 为oracle
properties.setProperty("helperDialect", "oracle");
//配置分页的合理化数据
properties.setProperty("reasonable", "true");
pageInterceptor.setProperties(properties);
//将拦截器设置到sqlSessionFactroy中
sqlSessionFactoryBean.setPlugins(new Interceptor[] {pageInterceptor});
return sqlSessionFactoryBean;
}
二、执行流程
/**
* 查询所有商品
* */
@PreAuthorize("hasAuthority('PRODUCT_LIST')")
@Transactional(propagation = Propagation.SUPPORTS ,readOnly = true)
public PageInfo findAllProduct(Integer pageNum,Integer pageSize){
//1.在调用dao查询前,先调用PageHelper的静态方法
PageHelper.startPage(pageNum, pageSize);
//2.调用dao查询
List<Product> products = productDao.findAllProduct();
//3.将查询以构造方式存入到PageHelper为我们提供的分页工具类PageInfo,返回给控制层
PageInfo pageInfo = new PageInfo(products);
return pageInfo;
};
1.
静态方法startPage执行插件会创建一个Page对象 存储当前的页码和每页条数 放在threadlocal变量中,和当前线程做绑定。
public abstract class PageMethod{
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
/**
* 设置 Page 参数
将生成的page绑定到当前线程
*
* @param page
*/
protected static void setLocalPage(Page page) {
LOCAL_PAGE.set(page);
}
/**
* 开始分页
*
* @param params
*/
public static <E> Page<E> startPage(Object params) {
Page<E> page = PageObjectUtil.getPageFromObject(params, true);
//当已经执行过orderBy的时候
Page<E> oldPage = getLocalPage();
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
//将生成的page绑定到当前线程
setLocalPage(page);
return page;
}
}
2.
由于我们在sqlSession中配置了PageInterceptor拦截器,所以sqlSession查询前会被该拦截器拦截,
拦截器会判断该查询是否需要分页,如果不需要分页,直接调用dao的sql进行执行,
如果需要分页,获取dao执行的sql语句,根据当前的数据库方言(我们这里用了oracle),得到分页的sql语句,执行改sql查询得到分页后的记录结果集
//该拦截器类,实现了mybatis的拦截器接口
public class PageInterceptor implements Interceptor
//判断是否需要进行分页查询
if (dialect.beforePage(ms, parameter, rowBounds)) {
//生成分页的缓存 key
CacheKey pageKey = cacheKey;
//处理参数对象
parameter = dialect.processParameterObject(ms, parameter, boundSql, pageKey);
//调用方言获取分页 sql
String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
//设置动态参数
for (String key : additionalParameters.keySet()) {
pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
//执行分页查询
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
} else {
//不执行分页的情况下,也不执行内存分页
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
}
例如当配置为oracle时,会根据我们dao原有的参数,和分页参数,生成oracle的分页sql语句
public class OracleDialect extends AbstractHelperDialect
public String getPageSql(String sql, Page page, CacheKey pageKey) {
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 120);
if (page.getStartRow() > 0) {
sqlBuilder.append("SELECT * FROM ( ");
}
if (page.getEndRow() > 0) {
sqlBuilder.append(" SELECT TMP_PAGE.*, ROWNUM ROW_ID FROM ( ");
}
sqlBuilder.append(sql);
if (page.getEndRow() > 0) {
sqlBuilder.append(" ) TMP_PAGE WHERE ROWNUM <= ? ");
}
if (page.getStartRow() > 0) {
sqlBuilder.append(" ) WHERE ROW_ID > ? ");
}
return sqlBuilder.toString();
}
3.
使用得到的记录结果集 从threadlocal变量中取出page对象 把集合赋值给page对象 此时page对象已经包含pageNum pageSize total List记录集合
类
public abstract class AbstractHelperDialect extends AbstractDialect implements Constant
//查询万结果集后
@Override
public Object afterPage(List pageList, Object parameterObject, RowBounds rowBounds) {
//1.从线程对象中获取oage对象
Page page = getLocalPage();
if (page == null) {
return pageList;
}
//2.将查询结果集添加到线程中的page对象中
page.addAll(pageList);
if (!page.isCount()) {
page.setTotal(-1);
} else if ((page.getPageSizeZero() != null && page.getPageSizeZero()) && page.getPageSize() == 0) {
page.setTotal(pageList.size());
} else if(page.isOrderByOnly()){
page.setTotal(pageList.size());
}
//3.将page返回
return page;
}
4.
这里我们来看下page对象,可以看到page对象继承了arraylist集合,所以调用addAll可以将查询结果添加到page对象中,而继承的另一个好处就是,根据多态的原理,该结果集对象page可以直接作为返回值,赋给我们在dao中的定义好的list集合,并返回给service业务层
public class Page<E> extends ArrayList<E> implements Closeable {
private static final long serialVersionUID = 1L;
/**
* 页码,从1开始
*/
private int pageNum;
/**
* 页面大小
*/
private int pageSize;
/**
* 起始行
*/
private int startRow;
/**
* 末行
*/
private int endRow;
/**
* 总数
*/
private long total;
/**
* 总页数
*/
private int pages;
/**
* 包含count查询
*/
private boolean count = true;
/**
* 分页合理化
*/
private Boolean reasonable;
/**
* 当设置为true的时候,如果pagesize设置为0(或RowBounds的limit=0),就不执行分页,返回全部结果
*/
private Boolean pageSizeZero;
/**
* 进行count查询的列名
*/
private String countColumn;
/**
* 排序
*/
private String orderBy;
/**
* 只增加排序
*/
private boolean orderByOnly;
5.
当servicee收到返回值后,我们通过构造将其封装到插件为我们提供的前端显示对象pageInfo中(page对象主要用于数据库查询的封装,pageInfo主要用于前端的显示),我们可以直接将pageInfo返回给jsp页面,在jsp页面中通过el表达式调用pageInfo的get方法取值。
这里我们看到pageInfo的构造方法,对page中的值进行了获取和封装
public class PageInfo<T> implements Serializable {
private static final long serialVersionUID = 1L;
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//由于startRow和endRow不常用,这里说个具体的用法
//可以在页面中"显示startRow到endRow 共size条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总记录数
private long total;
//总页数
private int pages;
//结果集
private List<T> list;
//前一页
private int prePage;
//下一页
private int nextPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;
public PageInfo(List<T> list) {
this(list, 8);
}
/**
* 包装Page对象
*
* @param list
page结果
* @param navigatePages 页码数量
*/
public PageInfo(List<T> list, int navigatePages) {
if (list instanceof Page) {
Page page = (Page) list;
this.pageNum = page.getPageNum();
this.pageSize = page.getPageSize();
this.pages = page.getPages();
this.list = page;
this.size = page.size();
this.total = page.getTotal();
//由于结果是>startRow的,所以实际的需要+1
if (this.size == 0) {
this.startRow = 0;
this.endRow = 0;
} else {
this.startRow = page.getStartRow() + 1;
//计算实际的endRow(最后一页的时候特殊)
this.endRow = this.startRow - 1 + this.size;
}
} else if (list instanceof Collection) {
this.pageNum = 1;
this.pageSize = list.size();
this.pages = this.pageSize > 0 ? 1 : 0;
this.list = list;
this.size = list.size();
this.total = list.size();
this.startRow = 0;
this.endRow = list.size() > 0 ? list.size() - 1 : 0;
}
if (list instanceof Collection) {
this.navigatePages = navigatePages;
//计算导航页
calcNavigatepageNums();
//计算前后页,第一页,最后一页
calcPage();
//判断页面边界
judgePageBoudary();
}
}
最后
以上就是鳗鱼大象为你收集整理的Spring插件之PageHelper(二)的执行原理1.2.3.5.的全部内容,希望文章能够帮你解决Spring插件之PageHelper(二)的执行原理1.2.3.5.所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复