MySQL格式化数据展示——分页查询

MySQL中,LIMIT配合OFFSET是实现分页查询最直接、最基本的方法。

核心逻辑非常简单:LIMIT N:表示我要取 N 条数据。OFFSET M:表示我要跳过前 M 条数据。

sql 复制代码
SELECT * FROM table_a ORDER BY id LIMIT 每页数量 OFFSET (当前页码-1)*每页数量;

举例: 想要查第 10 页的数据(每页 10 条),SQL 就是 LIMIT 10 OFFSET 90

在实际项目开发中,有个专门用于分页查询的插件PageHelper,需要先在pom.xml文件中添加依赖项,并在application.yml文件中添加配置

XML 复制代码
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.6</version>
</dependency>
XML 复制代码
pagehelper:
  helper-dialect: mysql  # 指定数据库类型
  reasonable: true        # 启用合理化,比如页码小于1自动查第一页
  support-methods-arguments: true # 支持直接传参

比如,在实体类bidding中,有字段id,customer_name, bid_deposit, agency_period,agency_power,package_agreement, contractual_terms, other_content,create_time, update_time,我现在希望它的结果按照创建时间排序并分页展示出来

前端提供当前页码page和每页展示的数据量pageSize,使用这个插件给这两个参数就可以自动完成分页,在查询的时候还是正常查询所有的数据,返回会自动给分页后的链表,PageHelper 是一个Mybatis的分页插件,本质是Mybatis的拦截器它会拦截Mybatis所有的查询SQL执行请求。

PageHelper插件在业务层的使用就是:

java 复制代码
@Service
@Slf4j
public class BiddingServiceImpl implements BiddingService {
    @Autowired
    private BiddingMapper bmapper;

    @Override
    public PageResult<Bidding> pageSelect(Integer page, Integer pageSize) {
        PageHelper.startPage(page,pageSize);
        List<Bidding> l=bmapper.selectBidding();
        Page<Bidding> p=(Page<Bidding>) l;
        return new PageResult<>(p.getTotal(),p.getResult());
    }
}
XML 复制代码
<mapper namespace="com.amaranth.mapper.BiddingMapper">
    <select id="selectBidding" resultType="com.amaranth.pojo.entity.Bidding">
        SELECT id,customer_name, bid_deposit, agency_period, agency_power,
               package_agreement, contractual_terms, other_content,
               create_time, update_time
        FROM users_db.bidding_db ORDER BY create_time DESC
    </select>

    <select id="getTotal" resultType="java.lang.Long">
        SELECT COUNT(*) FROM users_db.bidding_db
    </select>
</mapper>

而这里之所以可以将List强转为Page,是因为Page在源码中是继承的ArrayList,很明显,ArrayList也是List的一个实现类,所以 Page 对象 可以向上转型 赋值给 List 类型的变量,这是完全符合 Java 语法的。Page里面封装了:总条数total、当前页pageNum、页大小pageSize、总页数pages等所有分页属性;源码里面还有个getResult()方法就是直接返回Page对象自己,getTotal()的值这个值就是 PageHelper自动帮忙执行的那条 count(0) SQL 的查询结果

前端需要提供当前页码page和每页展示的数据量pageSize,通过前端传递的page和pageSize,PageHelper就可以拦截所有的SQL请求,后端返回的是展示的这一页的链表和数据总数total,并封装在一个PageResult类中。

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult<T> {
    private Long total;
    private List<T> rows;
}

这个rows链表可以通过selectBidding获取,而总数通过getTotal返回。由于数据量可能比较大,id用的是Long类型,所以计算当前记录的行数时要注意数据类型转换。在业务层将需要返回的当页记录链表和总记录数封装在一个PageResult类中,返回给controller层

如果没有传递page和pageSize时给默认值,在controller层接收参数时添加@RequestParam注解这里当前页码page设置为1,每页展示条数pageSize设置为5,这个@RequestParam注解的默认值也只能在controller层给定。而业务层返回来的PageResult作为Result中的Data返回给前端。

XML 复制代码
@RestController
@RequestMapping("/v1/bidding")
@Slf4j
public class BiddingController {
    @Autowired
    private BiddingService bservice;

    @GetMapping("/pageSelect")
    public Result selectBidding(@RequestParam(defaultValue = "1") Integer page,
                                @RequestParam(defaultValue = "5") Integer pageSize){
        log.info("请求参数:page={},pageSize={}",page,pageSize);
        try {
            // 调用业务层查询方法
            PageResult<Bidding> presult = bservice.pageSelect(page,pageSize);
            // 查询成功:返回【成功提示】,有数据
            return Result.success(presult);
        } catch (Exception e) {
            // 查询失败:捕获所有异常,返回【失败提示】,提示信息为异常原因
            return Result.fail("获取招投标信息失败:" + e.getMessage());
        }
    }
}

在IDEA的http.test中测试即可看到code,msg,data

尽管使用插件看起来很方便,但它的性能比较差,PageHelper 默认用法低效执行逻辑SQL查询,PageHelper的核心使用方式是:先执行全量查询,再在内存中做分页,PageHelper 会让 Mybatis 执行 SELECT 字段 FROM 表 → 查询表里的所有数据,把全表数据全部加载到 Java 内存中,然后再做拦截和筛选,这是它效率低的根本原因。可以在apifox上看到响应时间:

这里是花费了426ms。

为了提高性能,自己手动实现分页查询在调用mapper层的时候,就应该传入两个参数currentRow和pageSize分别表示当前记录行数和每页记录数,有了这两个参数就可以实现分段提取数据,而不是像PageHelper那样查询全部然后筛选

XML 复制代码
<mapper namespace="com.amaranth.mapper.BiddingMapper">
    <select id="selectBidding" resultType="com.amaranth.pojo.entity.Bidding">
        SELECT id,customer_name, bid_deposit, agency_period, agency_power,
           package_agreement, contractual_terms, other_content,
           create_time, update_time
        FROM users_db.bidding_db ORDER BY create_time DESC
        LIMIT #{currentRow}, #{pageSize}
    </select>

    <select id="getTotal" resultType="java.lang.Long">
        SELECT COUNT(*) FROM users_db.bidding_db
    </select>
</mapper>
XML 复制代码
@Mapper
public interface BiddingMapper {
    List<Bidding> selectBidding(@Param("currentRow") Long currentRow,
                                @Param("pageSize") Integer pageSize);

    Long getTotal();
}
XML 复制代码
@Service
@Slf4j
public class BiddingServiceImpl implements BiddingService {
    @Autowired
    private BiddingMapper bmapper;

    @Override
    public PageResult<Bidding> pageSelect(Integer page, Integer pageSize) {
        Long currentRow=(long) (page-1)*pageSize;
        PageResult<Bidding> presult=new PageResult<>();
        List<Bidding> list=bmapper.selectBidding(currentRow,pageSize);
        log.info("list={}",list);
        Long total=bmapper.getTotal();
        log.info("total={}",total);
        presult.setRows(list);
        presult.setTotal(total);
        return presult;
    }
}

业务层在计算当前记录行数时,由于只能接收到controller层传过来的当前页面page和每页记录数pageSize,那么当前行数就是(page-1)*pageSize,需要注意类型转换。

那么就看这次的响应时间:

这两条 SQL 都是纯原生、无任何额外开销的查询,数据库能走索引、精准命中,执行效率拉满,所以耗时只有 34ms,这是最推荐的分页写法。

相关推荐
风流倜傥唐伯虎15 分钟前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
Yvonne爱编码25 分钟前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚26 分钟前
JAVA进阶之路——无奖问答挑战1
java·开发语言
你这个代码我看不懂34 分钟前
@ConditionalOnProperty不直接使用松绑定规则
java·开发语言
杜子不疼.38 分钟前
CANN_Transformer加速库ascend-transformer-boost的大模型推理性能优化实践
深度学习·性能优化·transformer
fuquxiaoguang1 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
ujainu1 小时前
Flutter + OpenHarmony 实现无限跑酷游戏开发实战—— 对象池化、性能优化与流畅控制
flutter·游戏·性能优化·openharmony·endless runner
琹箐1 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
__WanG1 小时前
JavaTuples 库分析
java
坚持就完事了1 小时前
数据结构之树(Java实现)
java·算法