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

news/2024/10/5 10:54:59/
一、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 # 指定数据库方言为MySQLreasonable: 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实体类:

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

对应的Mapper接口:

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

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//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进行分页查询:

java">
@Service
public class UserService {@Autowiredprivate 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接口来调用分页查询:

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

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

三、问题解决

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

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

(2)分页参数不生效

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

(3)性能问题

java">对于大数据量的表,分页查询可能会带来性能问题。可以通过增加索引、优化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()方法的功能是:
在这里插入图片描述


http://www.ppmy.cn/news/1473487.html

相关文章

android 定时调用方法

在Android中&#xff0c;可以使用Handler类和Runnable接口来实现定时调用方法。以下是一个简单的例子&#xff0c;展示了如何每隔一定时间调用一个方法。 import android.os.Handler; import android.os.SystemClock; import androidx.appcompat.app.AppCompatActivity; impor…

开发必备基础知识【字符编码合集】

开发必备基础知识【字符编码合集】 大家在日常开发交流中会发现&#xff0c;别人那里运行的好好的文件&#xff0c;在你电脑上却无法编译&#xff0c;甚至出现一堆莫名其妙的字符&#xff0c;比如&#xff1a;&#xfffd;&#xfffd;&#xfffd; 程序中经常遇到一些关于乱码…

单线服务器有什么作用?

什么是单线服务器&#xff1f;单线服务器是指只有一条物理线路可以接入的服务器&#xff0c;这表明所有的数据信息与用户的访问请求都只能通过这一条线路来进行传输&#xff0c;因此单线服务器在服务器的性能与可扩展性方面有着一定的限制。 单线服务器与双线服务器相比&#x…

构建LangChain应用程序的示例代码:49、如何使用 OpenAI 的 GPT-4 和 LangChain 库实现多模态问答系统

! pip install "openai>1" "langchain>0.0.331rc2" matplotlib pillow加载图像 我们将图像编码为 base64 字符串&#xff0c;如 OpenAI GPT-4V 文档中所述。 import base64 import io import osimport numpy as np from IPython.display import HT…

2.3.2 主程序和外部IO交互 (文件映射方式)----IO Client实现

2.3.2 主程序和外部IO交互 &#xff08;文件映射方式&#xff09;----IO Client C实现 和IOServer主要差别&#xff1a; 1 使用Open_Client 连接 2 一定要先打开IOServer&#xff0c;再打开IO_Client 效果显示 1 C 代码实现 1.1 shareddataClient.h 头文件中引用 和sharedd…

深度解析Java世界中的对象镜像:浅拷贝与深拷贝的奥秘与应用

在Java编程的浩瀚宇宙中&#xff0c;对象拷贝是一项既基础又至关重要的技术。它直接关系到程序的性能、资源管理及数据安全性。然而&#xff0c;提及对象拷贝&#xff0c;不得不深入探讨其两大核心类型&#xff1a;浅拷贝&#xff08;Shallow Copy&#xff09;与深拷贝&#xf…

【网络安全】第6讲 黑客与网络攻击(笔记)

一、黑客简介 1、什么是黑客 2、黑客的产生 &#xff08;1&#xff09;任何系统都不可能绝对安全 &#xff08;2&#xff09;人类的好奇心 &#xff08;3&#xff09;经济利益驱使 黑客产业链各个角色分工明确&#xff1a;老板、病毒编写者&#xff0c;流量商&#xff0c;盗…

散度的可视化

散度的可视化 flyfish 向量场和散度 假设我们有一个简单的向量场&#xff1a; F ( x , y , z ) \mathbf{F} (x, y, z) F(x,y,z)在这里&#xff0c;向量场 F \mathbf{F} F 是由三个分量组成的向量&#xff0c;每个分量是空间坐标 x x x、 y y y、 z z z 的函数&#xff…