总结一下:分页的几种办法

  • Post author:
  • Post category:其他

总结一下:分页的几种办法

以mysql为例,做分页的方法,目前我总结了3种。

第一种分页:采用Query类和PageUtils类做出分页,sql用limit获取条数

第一步:Query 类的作用是对传入的分页参数做处理,并计算出当前页的起始和结束条数

@Data
public class Query extends LinkedHashMap<String, Object> {
	private static final long serialVersionUID = 1L;
	//当前页码
    private int page;
    //每页条数
    private int limit;
    public Query(Map<String, Object> params){
        this.putAll(params);
        //分页参数
        this.page = Integer.parseInt(params.get("page").toString());
        this.limit = Integer.parseInt(params.get("limit").toString());
        this.put("offset", (page - 1) * limit);
        this.put("page", page);
        this.put("limit", limit);
    }
}

第二步:根据查询的结果计算总页数

@Data
public class PageUtils implements Serializable {
	private static final long serialVersionUID = 1L;
	//总记录数
	private int totalCount;
	//每页记录数
	private int pageSize;
	//总页数
	private int totalPage;
	//当前页数
	private int currPage;
	//列表数据
	private List<?> list;
	
	/**
	 * 分页
	 * @param list        列表数据
	 * @param totalCount  总记录数
	 * @param pageSize    每页记录数
	 * @param currPage    当前页数
	 */
	public PageUtils(List<?> list, int totalCount, int pageSize, int currPage) {
		this.list = list;
		this.totalCount = totalCount;
		this.pageSize = pageSize;
		this.currPage = currPage;
		this.totalPage = (int)Math.ceil((double)totalCount/pageSize);
	}
}

第三步: java代码中使用分页

		//对传入分页的start、limit作处理,计算出要查看也页的起始结束条数分别是多少
 		Query query = new Query(params);
 		//去数据库查询,带上计算好的分页数据(即limit所需参数)
        List<Bug> list = bugService.queryList(query);
        //查询总条数
        int total = bugService.queryTotal(query);
        //根据总条数、当前页、等数据调用PageUtils 计算出总页数
        PageUtils pageUtil = new PageUtils(list, total, query.getLimit(), query.getPage());
        return R.ok().put("page", pageUtil).put("total", total);

查询sql的写法:

 <if test="offset != null and limit != null">
            limit #{offset}, #{limit}
        </if>

这个方法简单明了,接近原生,更好理解原理,缺点是代码多,要单独查总记录数

第二种分页:采用插件pagehelper(前面博客有讲,这里简单过一下)

第一步:导包

 <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.5</version>
        </dependency>

第二步:配置yml

pagehelper:
   #指定数据库	可以不配置,插件会自动检测数据库的类型
  helper-dialect: mysql
  #分页合理化参数,默认值为false,当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
  reasonable: true
  #分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页
  support-methods-arguments: true
  #用于从对象中根据属性名取值	可以配置pageNum,pageSize,count,pageSizeZero,reasonable。不配置映射的用默认值。
  params: count=countSql

第三步:分页(只列出关键代码)

 		int start = Integer.parseInt(String.valueOf(map.get("start")));
        int limit = Integer.parseInt(String.valueOf(map.get("limit")));
        PageHelper.startPage(start,limit);
        PageInfo<HonorEntity> pageInfo = new PageInfo<>(pageHelperServiceImpl.pageHonor());

这样查出来的代码分页的参数齐全的。
不需要单独查总数,简单易用。
注意:
使用spring boot2整合 pagehelper-spring-boot-starter必须排除一下依赖
因为pagehelper-spring-boot-starter也已经在pom依赖了mybatis与mybatis-spring
所以会与mybatis-plus-boot-starter中的mybatis与mybatis-spring发生冲突

 <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>${pagehelper.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

深坑再现: 最近遇到使用pageHelper总数不正确的问题,总是随前端传的分页参数变化而变化。原因是:
PageInfo<HonorEntity> pageInfo = new PageInfo<>(pageHelperServiceImpl.pageHonor());pageInfo
中的数据又做了流式数据处理导致获取到的list的size.(比如这里的a就是真实去数据库查询的list集合)
参看pageHelper的底层:PageSerializable类的构造
public PageSerializable(List list) {
this.list = list;
if (list instanceof Page) {
this.total = ((Page)list).getTotal();
} else {
this.total = (long)list.size();
}
}
即:本来pageInfo 是Page类型,里面封装了total,如果做了其他处理就成了List类型即,total
获取到的是list的大小了。

第三种:mybatis-plus自带的分页

在项目使用mybatis-plus时使用它自己的分页即可。
第一步:导入mybatis-plus的包(以后项目尽量都导它,因为它和mybatis兼容的,用不用都可以)

<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.1.2</version>
		</dependency>

第二步:写一个关于分页的配置类(只有中间那个paginationInterceptor是关于分页的,这里是官网写法,最简单就是直接返回对象,其他都不用配置)

@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {
    /**
     * 注册乐观锁
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
    /**
    *分页配置
    **/
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
    /**
     * SQL执行效率插件
     */
    @Bean
    @Profile({"dev","test"})// 设置 dev test 环境开启
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        //mysql执行的最大时间,如果超过1毫秒则不执行,并抛出异常
        performanceInterceptor.setMaxTime(1000);
        performanceInterceptor.setFormat(true);
        return performanceInterceptor;
    }
}

第三步:代码中的使用(这里是在实现类调用dao层的方法)

 public R pageNationQuery(int start,int limit) {
        IPage<LiaochaoUser> page = new Page<>(start,limit);
       page =  liaochaoUserMapper.selectPage(page, null);
        return R.ok("查询成功").put("data",page);
    }

总结:
三种方法都可以,都不错,第一种虽然代码多但是它轻量啊,群智曾使用。
第二种,方便简单,当初傻傻找查总记录数是哪个sql,始终找不到,联通曾使用。
第三种是以后的趋势,是mybatis-plus的附属功能,网络态势项目使用。

高效查询是否存在和高效分页

1、查询是否存在还在用count(*)吗? out了!

count(0):将返回表格中所有存在的行的总数包括值为null的行 count(1):会统计包括null值的所有符合条件的字段的条数。
count(0)count(1) count(*) 都会返回null的行
count(字段):将返回表格中除去null以外的所有行的总数(有默认值的列也会被计入)
按效率排序的话,count(字段)<count(主键 id)<count(1)≈count(*)

高效查询是否存在:

SELECT 1 FROM sys_user WHERE id = 1000009 LIMIT 1

SQL不再使用count,而是改用LIMIT 1,让数据库查询时遇到一条就返回,不要再继续查找还有多少条了
业务代码中直接判断是否非空即可,存在会返回1,不存在会返回null

2、高效分页

传统分页:select * from table limit 10000,10;
偏移量越大则越慢
推荐分页:select * from table WHERE id>=23434 limit 11;

limit #{offset}, #{limit}后的两个参数是:
#{offset}起始页,它是从0开始的,第一页就是取0
#{limit}每页条数
如果只查询前几条的话,分页参数只需要一个即可
例如:select * from t_user limit 5它代表查询表中前5条


版权声明:本文为m0_37635053原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。