关于JAVA分页新知识

2023年 10月 12日 56.2k 0

DBA 讨厌JAVA分页插件,随手写个分页代码

昨天我写了个分页服务类,今天发到群里,JAVA同事说,他开发个新分页插件,不会调用COUNT(*).

他说在JAVA拦截器做!

我们发现个非常特别的MYSQL函数 

SELECT SQL_CALC_FOUND_ROWS * FROM TABLE_A WHERE A.ID=? ....

 开源的pagehelp是分两条sql,而他就一条,实际发往数据库也是一条,

开发人员还是写一条SQL,而PageHelp插件实际发两条SQL到数据库.

而他下面通过另外个函数返回总数! 

select FOUND_ROWS();

从他的部分JAVA拦截器代码来看,好像没有考虑下一页,也要统计一次总行数!

大家猜猜 上面那个SQL 在数据库的执行行为如何?

SELECT SQL_CALC_FOUND_ROWS * FROM TABLE_A WHERE A.ID=? .... limit 10,10;

1 要返回总数, 2要返回前第20条数据.

在说出答案之间,我们还是看看开源插件的使用方式

@Override
    public List selectUserReportInfolistPage(UserReportInfoVo condition, Pagination pagination)
    {
        if (null != pagination)
        {
            PageHelper.startPage(pagination.getPage(), pagination.getRows());
            List resultList = userReportInfoMapper.selectByExample(this.makeExample(condition));
            PageInfo pageInfo = new PageInfo(resultList);
            pagination.setTotal((int) pageInfo.getTotal());
            pagination.setTotalPage(pageInfo.getPages());
            return resultList;
        } else 
        {
            List resultList = usertReportInfoMapper.selectByExample(this.makeExample(condition));
            return resultList;
        }
    }

看起来行数比较少, 没有注释是比较难看得懂的,userReportInfoMapper

Mapper 文件接口类,是没有selectByExample 的方法的
this.makeExample(condition) 是本服务类的一个方法,做啥用的? 

好像是拼接,又好像是!
总之搞个分页插件,套了一层,又一层!

快速过下眼看下分页插件几个类

public class PageInfo {
    private int pages;
    private int pageSize;
    private int total;
    private List list;
    
    public int getPages() {        return pages;    }
    public void setPages(int pages) {        this.pages = pages;    }
    public int getPageSize() {        return pageSize;    }
    public void setPageSize(int pageSize) {        this.pageSize = pageSize;    }
    public int getTotal() {        return total;    }
    public void setTotal(int total) {        this.total = total;    }
    public List getList() {        return list;    }
    public void setList(List list) {        this.list = list;    }
}


@ApiModel
public class Pagination implements Serializable, IPagination {
 private static final long serialVersionUID = 1L;
 public static final int DEFAULT_PAGE_SIZE=15;
 public static final int DEFAULT_CURRENT_PAGE=1;
 public static final int DEFAULT_SKIP_SIZE=10;
 public static final int DEFAULT_CURRENT_SKIP=1;
 public static final int MAX_PAGE_SIZE=200;

 private int rows = DEFAULT_SKIP_SIZE; 
 private int total;  
 private int totalPage ; 
 private int page= DEFAULT_CURRENT_SKIP; 
 private int limit = DEFAULT_SKIP_SIZE;
 private int offset;

 public int getRows() {
  return rows;
 }
 public void setRows(int rows) {
  if(100 Limit: 10 row(s) (no early end due to SQL_CALC_FOUND_ROWS)  (cost=216260.12 rows=10) (actual time=3.261..100830.348 rows=10 loops=1)
    -> Table scan on A  (cost=216260.12 rows=1911462) (actual time=3.254..100736.654 rows=399136 loops=1)

执行计划基本都一样, EXPALIN 是看不出来的, 只能使用高级执行计划

EXPALIN ANALYZE

最里面是

Table scan on A  (cost=216260.12 rows=1911462) (actual time=3.254..100736.654 rows=399136 loops=1)

估算是191.万行,实际扫描了39..9万行 然后返回给MYSQL的SERVER

显然它要扫描全部行,然后再LIMIT

如果带有条件也如此, 比如符号条件有1万行, 返回前10行. 也是如此在引擎层必须扫描1万行,要不然怎么获得总数呢?

显然这种方法不好! 最后还涉及到LEFT JOIN 多表关联 COUNT是多余的关联.

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论