从 500 错误到真相:解密“分页查询失败:null”的幕后黑手 !!!
🚨 从 500 错误到真相:解密“分页查询失败:null”的幕后黑手 🌟
嘿,技术探险家们!👋 今天我们要破解一个让人头秃的 bug:一个分页查询接口,返回了神秘的 500
错误,提示 "分页查询失败:null"
!😱 这篇文章将带你从现象入手,深入代码,揪出罪魁祸首,还有流程图助阵,快系好安全带,跟我一起 debug 吧!💪
🎯 第一幕:bug 的“犯罪现场”
问题复现
我在测试一个分页接口,输入是这样的 JSON:
{
"field": null,
"page": 0,
"size": null,
"value": null
}
结果,服务器甩给我这个:
{
"code": 500,
"msg": "分页查询失败:null",
"data": null
}
啥?500
是服务器内部错误,但 "null"
是啥意思?没有具体线索,我一脸懵。🤦♂️
🔍 第二幕:嫌疑人登场
代码一览
先看看控制器:
@PostMapping("/listInviteCodeByPageWithSearch")
public BaseResult listInviteCodeByPageWithSearch(
@SessionAttribute(value = Constants.ADMIN_ID) Integer adminId,
@Valid @RequestBody PageWithSearch pageWithSearch) {
try {
adminId = adminCommonService.getVipIdByProduct(adminId);
Page<InviteCode> inviteCodePage = inviteCodeService.findPaginatedInviteCodeByAdminIdAndSearch(adminId, pageWithSearch);
return BaseResult.success(inviteCodePage);
} catch (Exception e) {
return BaseResult.failure(500, "分页查询失败:" + e.getMessage());
}
}
服务层:
public Page<InviteCode> findPaginatedInviteCodeByAdminIdAndSearch(Integer adminId, PageWithSearch pageWithSearch) {
PageRequest pageRequest = PageRequest.of(pageWithSearch.getPage(), pageWithSearch.getPageSize());
String field = pageWithSearch.getField();
String value = pageWithSearch.getValue();
if (!StringUtils.isEmpty(field) && !StringUtils.isEmpty(value)) {
return inviteCodeRepository.findPaginatedInviteCodeByAdminIdAndFieldAndValue(adminId, field, value, pageRequest);
} else {
return inviteCodeRepository.findByAdminId(adminId, pageRequest);
}
}
PageWithSearch
(关键部分):
public class PageWithSearch extends BasePage {
private String field;
private String value;
public Integer getPageSize() {
return this.size;
}
public Pageable toPageableWithDefault(Integer page, Integer size) {
this.page = this.page == null ? page : this.page;
this.size = this.size == null ? size : this.size;
return PageRequest.of(this.page, this.size);
}
}
BasePage
(关键部分):
public class BasePage {
Integer page;
Integer size;
public Integer getPage() {
return page;
}
public Integer getSize() {
return size;
}
public Pageable toPageable() {
page = page != null ? page : 0;
size = size != null ? size : 9999;
return PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdDate"));
}
}
初步猜测
-
错误来源:
"分页查询失败:null"
来自e.getMessage()
,说明捕获了一个异常,但异常信息是null
。 -
嫌疑人:
size: null
很可疑,会不会是分页参数的问题?
🐞 第三幕:锁定真凶
线索分析
-
输入处理:
-
field: null, value: null
→ 进入findByAdminId
分支。 -
page: 0, size: null
→getPage()
返回0
,getPageSize()
返回null
。
-
-
关键代码:
PageRequest pageRequest = PageRequest.of(pageWithSearch.getPage(), pageWithSearch.getPageSize());
-
PageRequest.of(int page, int size)
需要int
。 -
getPageSize()
返回Integer
,且是null
。
-
真相揭晓!💡
-
自动拆箱:
-
getPageSize()
返回null
(Integer
类型)。 -
PageRequest.of
期望int
,Java 自动拆箱:null.intValue()
。 - 调用
null
的方法 →NullPointerException
。
-
-
异常处理:
-
catch (Exception e)
捕获异常。 -
NullPointerException
的getMessage()
是null
,拼接后变成"分页查询失败:null"
。
-
Mermaid 流程图:错误发生过程
🔧 第四幕:解决问题
为什么会这样?
-
PageWithSearch
的设计:-
getPageSize()
直接返回this.size
,没有防御null
。 -
toPageableWithDefault
提供了默认值,但服务层没用。
-
-
BasePage
的设计:-
toPageable()
也有默认值(size=9999
),但也没被调用。
-
-
服务层的疏忽:
- 直接用
getPageSize()
,没有检查null
。
- 直接用
修复方案
方案 1:用 toPageableWithDefault
修改服务层:
public Page<InviteCode> findPaginatedInviteCodeByAdminIdAndSearch(Integer adminId, PageWithSearch pageWithSearch) {
Pageable pageable = pageWithSearch.toPageableWithDefault(0, 10);
String field = pageWithSearch.getField();
String value = pageWithSearch.getValue();
if (!StringUtils.isEmpty(field) && !StringUtils.isEmpty(value)) {
return inviteCodeRepository.findPaginatedInviteCodeByAdminIdAndFieldAndValue(adminId, field, value, pageable);
} else {
return inviteCodeRepository.findByAdminId(adminId, pageable);
}
}
-
效果:
size: null
时,默认用 10。
方案 2:手动检查
public Page<InviteCode> findPaginatedInviteCodeByAdminIdAndSearch(Integer adminId, PageWithSearch pageWithSearch) {
Integer page = pageWithSearch.getPage() != null ? pageWithSearch.getPage() : 0;
Integer size = pageWithSearch.getPageSize() != null ? pageWithSearch.getPageSize() : 10;
if (size <= 0) {
throw new IllegalArgumentException("每页大小必须大于0");
}
PageRequest pageRequest = PageRequest.of(page, size);
String field = pageWithSearch.getField();
String value = pageWithSearch.getValue();
if (!StringUtils.isEmpty(field) && !StringUtils.isEmpty(value)) {
return inviteCodeRepository.findPaginatedInviteCodeByAdminIdAndFieldAndValue(adminId, field, value, pageRequest);
} else {
return inviteCodeRepository.findByAdminId(adminId, pageRequest);
}
}
方案 3:校验输入
修改 BasePage
:
public class BasePage {
@NotNull(message = "页码不能为空")
Integer page = 0;
@NotNull(message = "每页大小不能为空")
@Min(value = 1, message = "每页大小必须大于0")
Integer size = 10;
// ... 其他代码 ...
}
-
效果:
@Valid
在控制器层拦截size: null
,返回 400。
Mermaid 流程图:修复过程
🌈 第五幕:经验与反思
学到了啥?💡
-
自动拆箱的陷阱:
-
Integer
到int
的转换遇到null
会抛NullPointerException
,要小心!
-
-
设计与使用的脱节:
-
BasePage
和PageWithSearch
提供了默认值工具,但没用等于白搭。
-
-
异常信息的重要性:
-
NullPointerException
的getMessage()
是null
,别指望它自带线索。
-
小建议 🌟
-
日志救命:
log.info("page: {}, size: {}", pageWithSearch.getPage(), pageWithSearch.getPageSize());
-
优先用工具方法:
-
toPageable()
或toPageableWithDefault()
是现成的防护网,别自己造轮子。
-
🎬 尾声
从 "分页查询失败:null"
到揭开自动拆箱的秘密,这场 500 错误的破案之旅让我对 Java 类型系统又爱又恨。希望这篇博客能帮你在分页开发中少踩坑!有问题欢迎留言,咱们一起聊技术!✌️
上一篇: 几秒钟就充满电!科学
下一篇: 暂无数据