🌟 03_MyBatis-Plus LambdaQueryWrapper
爆出空指針異常的坑點分析
? 場景描述
來看一段常見的 MyBatis-Plus 查詢寫法,是否存在問題?
Page<VideoInfoVo> videoInfoVosPage = videoMapper.selectPage(page, new LambdaQueryWrapper<VideoInfoVo>().eq(videoAdtReq.getUserId() != null, VideoInfoVo::getUserId, videoAdtReq.getUserId()).ge(videoAdtReq.getStartTime() != null, VideoInfoVo::getUpdateTime, videoAdtReq.getStartTime().atStartOfDay()).le(videoAdtReq.getEndTime() != null, VideoInfoVo::getUpdateTime, videoAdtReq.getEndTime().atTime(LocalTime.MAX))
);
💥 問題分析:會爆出 NullPointerException!
🔍 錯誤點在哪?
.ge(videoAdtReq.getStartTime() != null, VideoInfoVo::getUpdateTime, videoAdtReq.getStartTime().atStartOfDay())
.le(videoAdtReq.getEndTime() != null, VideoInfoVo::getUpdateTime, videoAdtReq.getEndTime().atTime(LocalTime.MAX))
表面上看已經做了非空判斷,似乎沒有問題,但實際上,這樣寫仍然可能爆出 NullPointerException
。
🚨 關鍵誤區:Java 的參數求值順序
Java 會在調用方法前,先對所有參數進行求值,然后再把這些值傳給方法體。
所以即使你寫了:
videoAdtReq.getStartTime() != null
Java 仍然會執行:
videoAdtReq.getStartTime().atStartOfDay()
如果 getStartTime()
返回的是 null
,那 .atStartOfDay()
立即爆 NPE
!
? 正確寫法:提前提取變量,避免鏈式空指針
LocalDateTime start = null;
LocalDateTime end = null;if (videoAdtReq.getStartTime() != null) {start = videoAdtReq.getStartTime().atStartOfDay();
}
if (videoAdtReq.getEndTime() != null) {end = videoAdtReq.getEndTime().atTime(LocalTime.MAX);
}Page<VideoInfoVo> videoInfoVosPage = videoMapper.selectPage(page, new LambdaQueryWrapper<VideoInfoVo>().eq(videoAdtReq.getUserId() != null, VideoInfoVo::getUserId, videoAdtReq.getUserId()).ge(start != null, VideoInfoVo::getUpdateTime, start).le(end != null, VideoInfoVo::getUpdateTime, end)
);
🧠 小結 Tips
寫法 | 是否安全 | 原因 |
---|---|---|
.eq(cond, field, value) | ? 安全 | 不涉及鏈式 null 調用 |
.ge(cond, field, value.atStartOfDay()) | ? 危險 | 即使 cond == false ,value.atStartOfDay() 已執行 |
📌 建議
- 所有可能為
null
的鏈式操作都要提前拆解; - 不要在
.ge()
、.le()
、.like()
等條件方法內部寫鏈式 null 調用; - 最好在寫查詢條件前,先準備好轉換后的數據變量。