项目实战--Spring Boot与PageHelper的集成及线程污染解决

一、PageHelper使用背景

公司要做个简单管理系统,要我搭建Spring Boot+MyBatis+PageHelper+Redis的项目框架然后交i给实习生来开发。这个其实很简单,但是遇到搭建和使用过程中PageHelper有好多小坑,就记录一下,避免再踩。
版本选择:

JDK 8
SpringBoot 2.5.0
MyBatis 3.5.7
PageHelper 5.2.0
二、步骤
2.1 新建Spring Boot项目

如果过程中,选择java版本时发现没有java8版本,只有java17和java21
在这里插入图片描述
原因:

spring2.X版本在20231124日停止维护,因此创建spring项目时不再有2.X版本的选项,只能从3.1.X版本开始选择
而Spring3.X版本不支持JDK8,JDK11,最低支持JDK17,因此JDK11也无法选择

解决:
目前阿里云支持创建Spring2.X版本的项目

修改Server URL为:https://start.aliyun.com

在这里插入图片描述
这样就可以创建啦

2.2 引入依赖

在pom.xml文件中添加相关依赖:

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!-- MyBatis Starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>
    <!-- PageHelper -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.4.0</version>
    </dependency>
</dependencies>
2.3 配置PageHelper

在application.yml文件中进行PageHelper的基本配置:

pagehelper:
  helper-dialect: mysql # 指定数据库方言为MySQL
  reasonable: true # 分页合理化,启用后如果页码<1则查询第一页,页码>总页数则查询最后一页。
  support-methods-arguments: true # 支持通过Mapper接口参数来传递分页参数
  params: count=countSql # 指定count查询的参数名称
2.4 配置MyBatis

让PageHelper与MyBatis集成,还需在SpringBoot配置文件中添加MyBatis的相关配置:

mybatis:
  mapper-locations: classpath:/mappers/*.xml # Mapper XML文件的位置
  type-aliases-package: com.example.demo.entity # 实体类的包路径
2.5 编写Mapper接口和XML

User实体类:

public class User {
    private Long id;
    private String name;
    private String email;
    // getters and setters
}

对应的Mapper接口:

public interface UserMapper {
    @Select("SELECT * FROM users")
    List<User> selectAll();
}

Mapper XML文件则包含分页查询的SQL:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <select id="selectAll" resultType="com.example.demo.entity.User">
        SELECT * FROM users
    </select>
</mapper>
2.6 使用PageHelper进行分页

Service层使用PageHelper进行分页查询:


@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    public PageInfo<User> getUsers(int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = userMapper.selectAll();
        return new PageInfo<>(users);
    }
}

在Controller层,通过RESTful接口来调用分页查询:


@RestController
@RequestMapping("/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping
    public PageInfo<User> getUsers(@RequestParam(defaultValue = "1") int pageNum,
                                   @RequestParam(defaultValue = "10") int pageSize) {
        return userService.getUsers(pageNum, pageSize);
    }
}

这就实现简单的分页查询功能,通过PageHelper来控制分页参数。

三、问题解决

(1)分页无效或查询结果为空

确保在调用分页查询方法前,已经正确调用了PageHelper.startPage方法。
检查数据库连接是否正常,SQL查询语句是否正确。

(2)分页参数不生效

检查Controller层是否正确接收并传递分页参数。
确保application.yml中配置的support-methods-arguments为true

(3)性能问题

对于大数据量的表,分页查询可能会带来性能问题。可以通过增加索引、优化SQL查询等方式提高性能。

(4)使用过程中线程污染,无缘故的分页
前端调用一个未分页的接口,出现数据丢失或者报错的情况:
现象:前端调用一个只查询一条数据的接口,该接口执行的SQL是:

select id,statistics_month,update_time from business_statistics_record
order by statistics_month desclimit 1

在这里插入图片描述
但是实际上,日记打印出来的SQL:limit 1 limit ?, ?;就出现查询异常:
在这里插入图片描述
经过排查,真正原因是因为调用自定义分页出现问题:PageHelper.startPage(pageNum, pageSize);调用之后并没有消费,分页参数一直保存在线程中,当这个线程再次调用的时候,导致莫名奇妙的加上limit关键字。
查看PageHelper源码看到:

PageHelper 方法使用静态的 ThreadLocal参数,分页参数和线程是绑定的。只要保证在 PageHelper方法调用后紧跟MyBatis查询方法,这就是安全的。因为 PageHelper在finally代码段中自动清除ThreadLocal存储的对象。

而随机加上limit关键字,查看ThreadLocal LOCAL_PAGE值的变化,只有当线程复用的时候才会出现LOCAL_PAGE已被实例化。
在这里插入图片描述
为避免使用PageHelper过程中如果出现无缘无故出现分页,
在使用了PageHelper.startPage()后需要紧接着 MyBatis 查询方法。
最好是在执行sql的方法加上finally语句清理page缓存:
在这里插入图片描述

这个afterAll()方法中:
在这里插入图片描述
而 clearPage()方法的功能是:
在这里插入图片描述

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

容若只如初见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值