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,这是最推荐的分页写法。

相关推荐
va学弟2 小时前
SQL 进阶知识——多表关联与约束
数据库·sql
Java后端的Ai之路2 小时前
【Java教程】- 并发编程核心知识解读
java·开发语言·并发编程
椰羊~王小美2 小时前
为什么@Builder 注解默认父类字段不可见
java
一 乐2 小时前
学生宿舍管理|基于springboot + vue学生宿舍管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·助农电商系统
一人の梅雨2 小时前
义乌购商品详情接口进阶实战:批发场景下的精准解析与高可用架构
java·服务器·前端
Dontla2 小时前
Mybatis Introduction (Java ORM Framework)
java·开发语言·mybatis
信码由缰2 小时前
JExten:基于Java模块系统(JPMS)构建健壮的插件架构
java·开发语言·架构
NuageL2 小时前
SpringBoot使用@Scheduled注解实现定时任务
java·spring boot·后端
heze092 小时前
sqli-labs-Less-23
数据库·mysql·网络安全